Compare commits
26 Commits
resourcing
...
resourcing
| Author | SHA1 | Date | |
|---|---|---|---|
| cce27c26f6 | |||
| 3211404397 | |||
| fbde2fd031 | |||
| ac9dae7c5b | |||
| 5183769a7c | |||
| 3301cf3d7a | |||
| 6913e9e4b5 | |||
| c5798b3127 | |||
| 2c94638e27 | |||
| b8ed577fea | |||
| e68a18bb89 | |||
| 862af44c03 | |||
| 71ea0ca5f2 | |||
| ee7efaef6a | |||
| bfb029058c | |||
| b7e774a556 | |||
| 2901d19db0 | |||
| 5c4dadbe7c | |||
| 6ac122c631 | |||
| 16559b94fe | |||
| 0698e14d36 | |||
| 3d9672154c | |||
| 55b4b1fa8d | |||
| b80968999e | |||
| 6bac89c536 | |||
| b9af7e0ca7 |
2
app.json
2
app.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "hipmi-mobile",
|
||||
"name": "HIPMI BADUNG",
|
||||
"slug": "hipmi-mobile",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
|
||||
9
app/(application)/(image)/preview-image/[id]/index.tsx
Normal file
9
app/(application)/(image)/preview-image/[id]/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LandscapeFrameUploaded, ViewWrapper } from "@/components";
|
||||
|
||||
export default function PreviewImage() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<LandscapeFrameUploaded />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export default function TakePicture() {
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
<StackCustom>
|
||||
<StackCustom >
|
||||
<ButtonCustom onPress={() => setUri(null)} title="Foto ulang" />
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
159
app/(application)/(user)/_layout.tsx
Normal file
159
app/(application)/(user)/_layout.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import { BackButton } from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
|
||||
export default function UserLayout() {
|
||||
return (
|
||||
<>
|
||||
<Stack screenOptions={HeaderStyles}>
|
||||
<Stack.Screen
|
||||
name="home"
|
||||
options={{
|
||||
title: "HIPMI",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push("/user-search")}
|
||||
/>
|
||||
),
|
||||
headerRight: () => (
|
||||
<Ionicons
|
||||
name="notifications"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push("/notifications")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Profile Section ========= */}
|
||||
<Stack.Screen
|
||||
name="profile"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Portofolio Section ========= */}
|
||||
<Stack.Screen
|
||||
name="portofolio"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== User Search Section ========= */}
|
||||
<Stack.Screen
|
||||
name="user-search/index"
|
||||
options={{
|
||||
title: "Pencarian Pengguna",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Notification Section ========= */}
|
||||
<Stack.Screen
|
||||
name="notifications/index"
|
||||
options={{
|
||||
title: "Notifikasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Event Section ========= */}
|
||||
<Stack.Screen
|
||||
name="event/(tabs)"
|
||||
options={{
|
||||
title: "Event",
|
||||
headerLeft: () => (
|
||||
<LeftButtonCustom path="/(application)/(user)/home" />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/detail/[id]"
|
||||
options={{
|
||||
title: "Event Detail",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Forum Section ========= */}
|
||||
<Stack.Screen
|
||||
name="forum/create"
|
||||
options={{
|
||||
title: "Tambah Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/forumku"
|
||||
options={{
|
||||
title: "Forumku",
|
||||
headerLeft: () => <BackButton icon={'close'} />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/index"
|
||||
options={{
|
||||
title: "Detail",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Maps Section ========= */}
|
||||
<Stack.Screen
|
||||
name="maps/index"
|
||||
options={{
|
||||
title: "Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/create"
|
||||
options={{
|
||||
title: "Tambah Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/[id]/custom-pin"
|
||||
options={{
|
||||
title: "Custom Pin Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== Marketplace Section ========= */}
|
||||
<Stack.Screen
|
||||
name="marketplace/index"
|
||||
options={{
|
||||
title: "Market Place",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export default function EventLayout() {
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarActiveTintColor: MainColor.yellow,
|
||||
tabBarInactiveTintColor: MainColor.white,
|
||||
tabBarInactiveTintColor: MainColor.white_gray,
|
||||
tabBarStyle: {
|
||||
backgroundColor: MainColor.darkblue,
|
||||
},
|
||||
37
app/(application)/(user)/forum/[id]/edit.tsx
Normal file
37
app/(application)/(user)/forum/[id]/edit.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ForumEdit() {
|
||||
const [text, setText] = useState("");
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
111
app/(application)/(user)/forum/[id]/forumku.tsx
Normal file
111
app/(application)/(user)/forum/[id]/forumku.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
AlertCustom,
|
||||
AvatarCustom,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
DrawerCustom,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDataDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Forumku() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<StackCustom>
|
||||
<CenterCustom>
|
||||
<AvatarCustom
|
||||
href={`/(application)/(image)/preview-image/${id}`}
|
||||
size="xl"
|
||||
/>
|
||||
</CenterCustom>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom bold truncate>
|
||||
@bagas_banuna
|
||||
</TextCustom>
|
||||
<TextCustom>1 postingan</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<ButtonCustom href={`/profile/${id}`}>
|
||||
Kunjungi Profile
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
{listDataDummyCommentarForum.map((e, i) => (
|
||||
<Forum_BoxDetailSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id as string}
|
||||
status={status}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Komponen Eksternal */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
onLeftPress={() => setAlertStatus(false)}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
onLeftPress={() => setDeleteAlert(false)}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
160
app/(application)/(user)/forum/[id]/index.tsx
Normal file
160
app/(application)/(user)/forum/[id]/index.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
import {
|
||||
AlertCustom,
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
export default function ForumDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
console.log(id);
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [text, setText] = useState("");
|
||||
|
||||
// Comentar
|
||||
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
|
||||
const [statusCommentar, setStatusCommentar] = useState("");
|
||||
|
||||
const dataDummy = {
|
||||
name: "Bagas",
|
||||
status: "Open",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 2,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{/* <StackCustom>
|
||||
</StackCustom> */}
|
||||
<Forum_BoxDetailSection
|
||||
data={dataDummy}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}
|
||||
/>
|
||||
<ButtonCustom
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Balas
|
||||
</ButtonCustom>
|
||||
|
||||
<Spacing height={40} />
|
||||
|
||||
{listDummyCommentarForum.map((e, i) => (
|
||||
<Forum_CommentarBoxSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawerCommentar}
|
||||
setStatus={setStatusCommentar}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id as string}
|
||||
status={status}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Status */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
|
||||
{/* Commentar */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
isVisible={openDrawerCommentar}
|
||||
closeDrawer={() => setOpenDrawerCommentar(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id as string}
|
||||
status={statusCommentar}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawerCommentar(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
37
app/(application)/(user)/forum/create.tsx
Normal file
37
app/(application)/(user)/forum/create.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ForumCreate() {
|
||||
const [text, setText] = useState("");
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Posting
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
128
app/(application)/(user)/forum/index.tsx
Normal file
128
app/(application)/(user)/forum/index.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
AlertCustom,
|
||||
AvatarCustom,
|
||||
BackButton,
|
||||
DrawerCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function Forum() {
|
||||
const id = "test-id-forum";
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <AvatarCustom href={`/forum/${id}/forumku`} />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari topik forum..."
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
}
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
router.navigate("/(application)/(user)/forum/create")
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{listDummyDiscussionForum.map((e, i) => (
|
||||
<Forum_BoxDetailSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
isTruncate={true}
|
||||
href={`/forum/${id}`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id}
|
||||
status={status}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Status */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
55
app/(application)/(user)/maps/[id]/custom-pin.tsx
Normal file
55
app/(application)/(user)/maps/[id]/custom-pin.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
MapCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import CenterCustom from "@/components/Center/CenterCustom";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function MapsCustomPin() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan maps ${id}`);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<StackCustom>
|
||||
<InformationBox text="Pin map akan secara otomatis menampilkan logo pada porotofolio ini, jika anda ingin melakukan custom silahkan upload logo pin baru anda." />
|
||||
<CenterCustom>
|
||||
<AvatarCustom size="xl" />
|
||||
</CenterCustom>
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => console.log("Upload")}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
|
||||
<BaseBox>
|
||||
<MapCustom />
|
||||
</BaseBox>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
59
app/(application)/(user)/maps/[id]/edit.tsx
Normal file
59
app/(application)/(user)/maps/[id]/edit.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
MapCustom,
|
||||
Spacing,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function MapsEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan maps ${id}`);
|
||||
router.back()
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||
|
||||
<BaseBox>
|
||||
<MapCustom />
|
||||
</BaseBox>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Pin"
|
||||
placeholder="Masukkan nama pin maps"
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload foto ");
|
||||
router.navigate(`/take-picture/${id}`);
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
59
app/(application)/(user)/maps/create.tsx
Normal file
59
app/(application)/(user)/maps/create.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function MapsCreate() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan maps ${id}`);
|
||||
router.replace(`/portofolio/${id}`);
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||
|
||||
<BaseBox style={{ height: 400 }}>
|
||||
<TextCustom>Maps Her</TextCustom>
|
||||
</BaseBox>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Pin"
|
||||
placeholder="Masukkan nama pin maps"
|
||||
/>
|
||||
|
||||
<Spacing height={50} />
|
||||
|
||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload foto ");
|
||||
router.navigate(`/take-picture/${id}`);
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
9
app/(application)/(user)/maps/index.tsx
Normal file
9
app/(application)/(user)/maps/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { MapCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function Maps() {
|
||||
return (
|
||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||
<MapCustom height={"100%"} />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
9
app/(application)/(user)/marketplace/index.tsx
Normal file
9
app/(application)/(user)/marketplace/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { TextCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function Marketplace() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<TextCustom>Marketplace</TextCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
118
app/(application)/(user)/notifications/index.tsx
Normal file
118
app/(application)/(user)/notifications/index.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
const categories = [
|
||||
{ id: 1, label: "Semua" },
|
||||
{ id: 2, label: "Event" },
|
||||
{ id: 3, label: "Job" },
|
||||
{ id: 4, label: "Voting" },
|
||||
{ id: 5, label: "Donasi" },
|
||||
{ id: 6, label: "Investasi" },
|
||||
{ id: 7, label: "Forum" },
|
||||
{ id: 8, label: "Collaboration" },
|
||||
];
|
||||
|
||||
const selectedCategory = (id: number) => {
|
||||
const category = categories.find((c) => c.id === id);
|
||||
return category?.label;
|
||||
};
|
||||
|
||||
const BoxNotification = ({
|
||||
index,
|
||||
activeCategory,
|
||||
}: {
|
||||
index: number;
|
||||
activeCategory: number | null;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<BaseBox
|
||||
onPress={() =>
|
||||
console.log(
|
||||
"Notification >",
|
||||
selectedCategory(activeCategory as number)
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>
|
||||
# {selectedCategory(activeCategory as number)}
|
||||
</TextCustom>
|
||||
|
||||
<View
|
||||
style={{
|
||||
borderBottomColor: MainColor.white_gray,
|
||||
borderBottomWidth: 1,
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextCustom truncate={2}>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint odio
|
||||
unde quidem voluptate quam culpa sequi molestias ipsa corrupti id,
|
||||
soluta, nostrum adipisci similique, et illo asperiores deleniti eum
|
||||
labore.
|
||||
</TextCustom>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom size="small" color="gray">
|
||||
{index + 1} Agustus 2025
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom size="small" color="gray">
|
||||
Belum lihat
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Notifications() {
|
||||
const [activeCategory, setActiveCategory] = useState<number | null>(1);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.id);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
return (
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<ScrollableCustom
|
||||
data={categories}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 20 }).map((e, i) => (
|
||||
<View key={i}>
|
||||
<BoxNotification index={i} activeCategory={activeCategory} />
|
||||
</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>
|
||||
);
|
||||
}
|
||||
191
app/(application)/(user)/portofolio/[id]/create.tsx
Normal file
191
app/(application)/(user)/portofolio/[id]/create.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
|
||||
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
|
||||
export default function PortofolioCreate() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
bidang_usaha: "",
|
||||
sub_bidang_usaha: "",
|
||||
alamat: "",
|
||||
nomor_telepon: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log("Selanjutnya");
|
||||
router.replace(`/maps/create`);
|
||||
}
|
||||
|
||||
const buttonSave = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={handleSave}>Selanjutnya</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSave}>
|
||||
{/* <TextCustom>Portofolio Create {id}</TextCustom> */}
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Lengkapi data bisnis anda." />
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), bidang_usaha: value });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<SelectCustom
|
||||
// disabled
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterSubBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.sub_bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), sub_bidang_usaha: value });
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
Nomor Telepon
|
||||
</TextCustom>
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Bisnis"
|
||||
placeholder="Masukkan deskripsi bisnis"
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
|
||||
autosize
|
||||
minRows={2}
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
{/* Logo */}
|
||||
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload logo >>", id);
|
||||
router.navigate(`/(application)/(image)/take-picture/${id}`);
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing height={40} />
|
||||
|
||||
{/* Social Media */}
|
||||
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
|
||||
<TextInputCustom
|
||||
label="Tiktok"
|
||||
placeholder="Masukkan username tiktok"
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Facebook"
|
||||
placeholder="Masukkan username facebook"
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Instagram"
|
||||
placeholder="Masukkan username instagram"
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Twitter"
|
||||
placeholder="Masukkan username twitter"
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Youtube"
|
||||
placeholder="Masukkan username youtube"
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
48
app/(application)/(user)/portofolio/[id]/edit-logo.tsx
Normal file
48
app/(application)/(user)/portofolio/[id]/edit-logo.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function PortofolioEditLogo() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Simpan logo ");
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<BaseBox
|
||||
style={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: 250,
|
||||
}}
|
||||
>
|
||||
<AvatarCustom size="xl" />
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.navigate(`/take-picture/${id}`)}
|
||||
>
|
||||
Update
|
||||
</ButtonCenteredOnly>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useLocalSearchParams, router } from "expo-router";
|
||||
|
||||
export default function PortofolioEditSocialMedia() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan sosmed ${id}`);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<TextInputCustom label="Tiktok" placeholder="Masukkan tiktok" />
|
||||
<TextInputCustom label="Instagram" placeholder="Masukkan instagram" />
|
||||
<TextInputCustom label="Facebook" placeholder="Masukkan facebook" />
|
||||
<TextInputCustom label="Twitter" placeholder="Masukkan twitter" />
|
||||
<TextInputCustom label="Youtube" placeholder="Masukkan youtube" />
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
151
app/(application)/(user)/portofolio/[id]/edit.tsx
Normal file
151
app/(application)/(user)/portofolio/[id]/edit.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
|
||||
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
|
||||
export default function PortofolioEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
bidang_usaha: "",
|
||||
sub_bidang_usaha: "",
|
||||
alamat: "",
|
||||
nomor_telepon: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log(`Update portofolio berhasil ${id}`);
|
||||
router.back();
|
||||
}
|
||||
|
||||
const buttonUpdate = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={handleSave}>Simpan</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonUpdate}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), bidang_usaha: value });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<SelectCustom
|
||||
// disabled
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterSubBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.sub_bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), sub_bidang_usaha: value });
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
Nomor Telepon
|
||||
</TextCustom>
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Bisnis"
|
||||
placeholder="Masukkan deskripsi bisnis"
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, deskripsi: value })
|
||||
}
|
||||
autosize
|
||||
minRows={2}
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
61
app/(application)/(user)/portofolio/[id]/index.tsx
Normal file
61
app/(application)/(user)/portofolio/[id]/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { DrawerCustom } from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { drawerItemsPortofolio } from "@/screens/Portofolio/ListPage";
|
||||
import Portofolio_MenuDrawerSection from "@/screens/Portofolio/MenuDrawer";
|
||||
import PorfofolioSection from "@/screens/Portofolio/PorfofolioSection";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function Portofolio() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
};
|
||||
const closeDrawer = () => {
|
||||
setIsDrawerOpen(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{/* Header */}
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
<PorfofolioSection />
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
<DrawerCustom
|
||||
isVisible={isDrawerOpen}
|
||||
closeDrawer={closeDrawer}
|
||||
height={350}
|
||||
>
|
||||
<Portofolio_MenuDrawerSection
|
||||
drawerItems={drawerItemsPortofolio({ id: id as string })}
|
||||
setIsDrawerOpen={setIsDrawerOpen}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
47
app/(application)/(user)/portofolio/[id]/list.tsx
Normal file
47
app/(application)/(user)/portofolio/[id]/list.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { BaseBox, Grid, TextCustom, ViewWrapper } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function ListPortofolio() {
|
||||
const { id } = useLocalSearchParams();
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
onPress={() => {
|
||||
console.log("press to Portofolio");
|
||||
router.push(`/portofolio/${id}`);
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
32
app/(application)/(user)/portofolio/_layout.tsx
Normal file
32
app/(application)/(user)/portofolio/_layout.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function PortofolioLayout() {
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
...HeaderStyles,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
}}
|
||||
>
|
||||
{/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */}
|
||||
<Stack.Screen
|
||||
name="[id]/create"
|
||||
options={{ title: "Tambah Portofolio" }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="[id]/list"
|
||||
options={{ title: "Daftar Portofolio" }}
|
||||
/>
|
||||
<Stack.Screen name="[id]/edit" options={{ title: "Edit Portofolio" }} />
|
||||
<Stack.Screen name="[id]/edit-logo" options={{ title: "Edit Logo " }} />
|
||||
<Stack.Screen
|
||||
name="[id]/edit-social-media"
|
||||
options={{ title: "Edit Social Media" }}
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
@@ -7,14 +8,15 @@ import {
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
|
||||
import InformationBox from "@/components/Box/InformationBox";
|
||||
import ButtonUpload from "@/components/Button/ButtonUpload";
|
||||
import LandscapeFrameUploaded from "@/components/Image/LandscapeFrameUploaded";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function CreateProfile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
email: "",
|
||||
@@ -24,20 +26,18 @@ export default function CreateProfile() {
|
||||
|
||||
const handlerSave = () => {
|
||||
console.log("data create profile >>", data);
|
||||
// router.back();
|
||||
router.back();
|
||||
};
|
||||
|
||||
const footerComponent = (
|
||||
<View style={GStyles.bottomBar}>
|
||||
<View style={GStyles.bottomBarContainer}>
|
||||
<ButtonCustom
|
||||
onPress={handlerSave}
|
||||
disabled={!data.name || !data.email || !data.address || !data.gender}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</View>
|
||||
</View>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={handlerSave}
|
||||
// disabled={!data.name || !data.email || !data.address || !data.gender}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -47,7 +47,12 @@ export default function CreateProfile() {
|
||||
<View style={{ alignItems: "center" }}>
|
||||
<AvatarCustom size="xl" />
|
||||
<Spacing />
|
||||
<ButtonUpload onPress={() => console.log("pressed")} />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.navigate(`/take-picture/${id}`)}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</View>
|
||||
|
||||
<Spacing />
|
||||
@@ -56,7 +61,12 @@ export default function CreateProfile() {
|
||||
<InformationBox text="Upload foto latar belakang anda." />
|
||||
<LandscapeFrameUploaded />
|
||||
<Spacing />
|
||||
<ButtonUpload onPress={() => console.log("pressed")} />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.navigate(`/take-picture/${id}`)}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</View>
|
||||
|
||||
<Spacing />
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { StyleSheet } from "react-native";
|
||||
@@ -44,15 +45,17 @@ export default function ProfileEdit() {
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
bottomBarComponent={
|
||||
<ButtonCustom
|
||||
disabled={
|
||||
!data.nama || !data.email || !data.alamat || !data.selectedValue
|
||||
}
|
||||
onPress={handleSave}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
footerComponent={
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
// disabled={
|
||||
// !data.nama || !data.email || !data.alamat || !data.selectedValue
|
||||
// }
|
||||
onPress={handleSave}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
}
|
||||
>
|
||||
<StackCustom gap={"xs"}>
|
||||
@@ -1,79 +1,28 @@
|
||||
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 LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { DRAWER_HEIGHT } from "@/constants/constans-value";
|
||||
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
||||
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, InteractionManager, TouchableOpacity } from "react-native";
|
||||
import React, { useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function Profile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [showLogoutAlert, setShowLogoutAlert] = useState(false);
|
||||
|
||||
const drawerItems: IMenuDrawerItem[] = [
|
||||
{
|
||||
icon: "create",
|
||||
label: "Edit profile",
|
||||
path: `/(application)/profile/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: "camera",
|
||||
label: "Ubah foto profile",
|
||||
path: `/(application)/profile/${id}/update-photo`,
|
||||
},
|
||||
{
|
||||
icon: "image",
|
||||
label: "Ubah latar belakang",
|
||||
path: `/(application)/profile/${id}/update-background`,
|
||||
},
|
||||
{
|
||||
icon: "add-circle",
|
||||
label: "Tambah portofolio",
|
||||
path: `/(application)/portofolio/${id}/create`,
|
||||
},
|
||||
// {
|
||||
// icon: "settings",
|
||||
// label: "Dashboard Admin",
|
||||
// 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)
|
||||
const drawerAnim = useRef(new Animated.Value(DRAWER_HEIGHT)).current; // mulai di luar bawah layar
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
Animated.timing(drawerAnim, {
|
||||
toValue: 0,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
Animated.timing(drawerAnim, {
|
||||
toValue: DRAWER_HEIGHT, // sesuaikan dengan tinggi drawer Anda
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}).start(() => {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
setIsDrawerOpen(false); // baru ganti state setelah animasi selesai
|
||||
});
|
||||
});
|
||||
setIsDrawerOpen(false);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
@@ -89,7 +38,7 @@ export default function Profile() {
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Profile",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
@@ -104,18 +53,16 @@ export default function Profile() {
|
||||
}}
|
||||
/>
|
||||
<ProfilSection />
|
||||
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
isVisible={isDrawerOpen}
|
||||
drawerAnim={drawerAnim}
|
||||
closeDrawer={closeDrawer}
|
||||
>
|
||||
<Profile_MenuDrawerSection
|
||||
drawerItems={drawerItems}
|
||||
drawerItems={drawerItemsProfile({ id: id as string })}
|
||||
setShowLogoutAlert={setShowLogoutAlert}
|
||||
setIsDrawerOpen={setIsDrawerOpen}
|
||||
/>
|
||||
@@ -1,25 +1,31 @@
|
||||
import { BaseBox, ButtonCustom } from "@/components";
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import ButtonUpload from "@/components/Button/ButtonUpload";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { Image } from "react-native";
|
||||
|
||||
export default function UpdateBackgroundProfile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Simpan foto background >>", id);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
bottomBarComponent={
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Simpan foto background >>", id);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
}
|
||||
>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<BaseBox
|
||||
style={{ alignItems: "center", justifyContent: "center", height: 250 }}
|
||||
>
|
||||
@@ -30,12 +36,12 @@ export default function UpdateBackgroundProfile() {
|
||||
/>
|
||||
</BaseBox>
|
||||
|
||||
<ButtonUpload
|
||||
title="Update"
|
||||
onPress={() =>
|
||||
router.navigate(`/(application)/take-picture/${id}`)
|
||||
}
|
||||
/>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.navigate(`/(application)/take-picture/${id}`)}
|
||||
>
|
||||
Update
|
||||
</ButtonCenteredOnly>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
47
app/(application)/(user)/profile/[id]/update-photo.tsx
Normal file
47
app/(application)/(user)/profile/[id]/update-photo.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function UpdatePhotoProfile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Simpan foto profile >>", id);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<BaseBox
|
||||
style={{ alignItems: "center", justifyContent: "center", height: 250 }}
|
||||
>
|
||||
<AvatarCustom size="xl" />
|
||||
</BaseBox>
|
||||
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Update photo >>", id);
|
||||
router.navigate(`/(application)/take-picture/${id}`);
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
{/* <Spacing />
|
||||
<ButtonCustom>Test</ButtonCustom> */}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
100
app/(application)/(user)/user-search/index.tsx
Normal file
100
app/(application)/(user)/user-search/index.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
ClickableCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function UserSearch() {
|
||||
function generateRandomPhoneNumber(index: number) {
|
||||
let prefix;
|
||||
|
||||
// Menentukan prefix berdasarkan index genap atau ganjil
|
||||
if (index % 2 === 0) {
|
||||
const evenPrefixes = ["6288", "6289", "6281"];
|
||||
prefix = evenPrefixes[Math.floor(Math.random() * evenPrefixes.length)];
|
||||
} else {
|
||||
const oddPrefixes = ["6285", "6283"];
|
||||
prefix = oddPrefixes[Math.floor(Math.random() * oddPrefixes.length)];
|
||||
}
|
||||
|
||||
// Menghitung panjang sisa nomor acak (antara 10 - 12 digit)
|
||||
const remainingLength = Math.floor(Math.random() * 3) + 10; // 10, 11, atau 12
|
||||
|
||||
// Membuat sisa nomor acak
|
||||
let randomNumber = "";
|
||||
for (let i = 0; i < remainingLength; i++) {
|
||||
randomNumber += Math.floor(Math.random() * 10); // Digit acak antara 0-9
|
||||
}
|
||||
|
||||
// Menggabungkan prefix dan sisa nomor
|
||||
return prefix + randomNumber;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari Pengguna"
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
{Array.from({ length: 20 }).map((e, index) => {
|
||||
return (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom href={`/profile/${index}`}/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<TextCustom size="large">Nama user {index}</TextCustom>
|
||||
<TextCustom size="small">
|
||||
+{generateRandomPhoneNumber(index)}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${index}`);
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</StackCustom>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,185 +1,28 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { BackButton } from "@/components";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function ApplicationLayout() {
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
headerTitleAlign: "center",
|
||||
contentStyle: {
|
||||
borderBottomColor: AccentColor.blue,
|
||||
borderBottomWidth: 2,
|
||||
},
|
||||
// headerLargeStyle: {
|
||||
// backgroundColor: MainColor.darkblue,
|
||||
// },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="home"
|
||||
options={{
|
||||
title: "HIPMI",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push("/(application)/user-search")}
|
||||
/>
|
||||
),
|
||||
headerRight: () => (
|
||||
<Ionicons
|
||||
name="notifications"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push("/(application)/notifications")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="forum/index"
|
||||
options={{
|
||||
title: "Forum",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="maps/index"
|
||||
options={{
|
||||
title: "Maps",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="marketplace/index"
|
||||
options={{
|
||||
title: "Market Place",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Profile */}
|
||||
<Stack.Screen
|
||||
name="profile"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Portofolio */}
|
||||
<Stack.Screen
|
||||
name="portofolio"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Event */}
|
||||
<Stack.Screen
|
||||
name="event/(tabs)"
|
||||
options={{
|
||||
title: "Event",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push("/(application)/home")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/detail/[id]"
|
||||
options={{
|
||||
title: "Detail",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* User Search */}
|
||||
<Stack.Screen
|
||||
name="user-search/index"
|
||||
options={{
|
||||
title: "Pencarian Pengguna",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Notification */}
|
||||
<Stack.Screen
|
||||
name="notifications/index"
|
||||
options={{
|
||||
title: "Notifikasi",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Stack screenOptions={HeaderStyles}>
|
||||
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
||||
|
||||
{/* Take Picture */}
|
||||
<Stack.Screen
|
||||
name="take-picture/[id]/index"
|
||||
name="(image)/take-picture/[id]/index"
|
||||
options={{
|
||||
title: "Ambil Gambar",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
),
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Preview Image */}
|
||||
<Stack.Screen
|
||||
name="(image)/preview-image/[id]/index"
|
||||
options={{
|
||||
title: "Preview Gambar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function Forum() {
|
||||
return (
|
||||
<>
|
||||
<View>
|
||||
<Text>Forum</Text>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function Maps() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Maps</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function MarketPlace() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Market Place</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function Notifications() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Notifications</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function PortofolioCreate() {
|
||||
const { id } = useLocalSearchParams();
|
||||
return (
|
||||
<View>
|
||||
<Text>Portofolio Create {id}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { BaseBox, ButtonCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import ButtonUpload from "@/components/Button/ButtonUpload";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { Image } from "react-native";
|
||||
|
||||
export default function UpdatePhotoProfile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
return (
|
||||
<ViewWrapper
|
||||
bottomBarComponent={
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Simpan foto profile >>", id);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
}
|
||||
>
|
||||
<BaseBox
|
||||
style={{ alignItems: "center", justifyContent: "center", height: 250 }}
|
||||
>
|
||||
<Image
|
||||
source={DUMMY_IMAGE.avatar}
|
||||
resizeMode="cover"
|
||||
style={{ width: 200, height: 200 }}
|
||||
/>
|
||||
</BaseBox>
|
||||
|
||||
<ButtonUpload
|
||||
title="Update"
|
||||
onPress={() => {
|
||||
console.log("Update photo >>", id);
|
||||
router.navigate(`/(application)/take-picture/${id}`);
|
||||
}}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export default function UserSearch() {
|
||||
return (
|
||||
<View>
|
||||
<Text>User Search</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Text, View } from "react-native";
|
||||
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Not Found</Text>
|
||||
</View>
|
||||
)
|
||||
<ViewWrapper>
|
||||
<StackCustom align="center" gap={0} style={{justifyContent: "center", alignItems: "center", flex: 1}}>
|
||||
<TextCustom size="large" bold style={{fontSize: 100}}>
|
||||
404
|
||||
</TextCustom>
|
||||
<TextCustom size="large" bold>
|
||||
Sorry, File Not Found
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -15,6 +15,7 @@ export default function RootLayout() {
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="index" options={{ title: "" }} />
|
||||
<Stack.Screen name="+not-found" options={{ title: "" }} />
|
||||
<Stack.Screen
|
||||
name="verification"
|
||||
options={{ title: "", headerBackVisible: false }}
|
||||
|
||||
21
bun.lock
21
bun.lock
@@ -11,6 +11,7 @@
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
@@ -31,7 +32,9 @@
|
||||
"react-native": "0.79.5",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
"react-native-maps": "1.20.1",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
"react-native-screens": "~4.11.1",
|
||||
@@ -237,6 +240,8 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.27.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q=="],
|
||||
|
||||
"@callstack/react-theme-provider": ["@callstack/react-theme-provider@3.0.9", "", { "dependencies": { "deepmerge": "^3.2.0", "hoist-non-react-statics": "^3.3.0" }, "peerDependencies": { "react": ">=16.3.0" } }, "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA=="],
|
||||
|
||||
"@egjs/hammerjs": ["@egjs/hammerjs@2.0.17", "", { "dependencies": { "@types/hammerjs": "^2.0.36" } }, "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A=="],
|
||||
|
||||
"@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
@@ -423,6 +428,8 @@
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
|
||||
|
||||
"@types/graceful-fs": ["@types/graceful-fs@4.1.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ=="],
|
||||
|
||||
"@types/hammerjs": ["@types/hammerjs@2.0.46", "", {}, "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw=="],
|
||||
@@ -697,6 +704,8 @@
|
||||
|
||||
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"decode-uri-component": ["decode-uri-component@0.2.2", "", {}, "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="],
|
||||
@@ -1373,8 +1382,12 @@
|
||||
|
||||
"react-native-is-edge-to-edge": ["react-native-is-edge-to-edge@1.1.7", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w=="],
|
||||
|
||||
"react-native-maps": ["react-native-maps@1.20.1", "", { "dependencies": { "@types/geojson": "^7946.0.13" }, "peerDependencies": { "react": ">= 17.0.1", "react-native": ">= 0.64.3", "react-native-web": ">= 0.11" }, "optionalPeers": ["react-native-web"] }, "sha512-NZI3B5Z6kxAb8gzb2Wxzu/+P2SlFIg1waHGIpQmazDSCRkNoHNY4g96g+xS0QPSaG/9xRBbDNnd2f2/OW6t6LQ=="],
|
||||
|
||||
"react-native-otp-entry": ["react-native-otp-entry@1.8.5", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-TZNkIuUzZKAAWrC8X/A22ZHJdycLysxUNysrGf0yTmDLRUyf4zLXwVFcDYUcRNe763Hjaf5qvtKGILb6lDGzoA=="],
|
||||
|
||||
"react-native-paper": ["react-native-paper@5.14.5", "", { "dependencies": { "@callstack/react-theme-provider": "^3.0.9", "color": "^3.1.2", "use-latest-callback": "^0.2.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-safe-area-context": "*" } }, "sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ=="],
|
||||
|
||||
"react-native-reanimated": ["react-native-reanimated@3.17.5", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4", "react-native-is-edge-to-edge": "1.1.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw=="],
|
||||
|
||||
"react-native-safe-area-context": ["react-native-safe-area-context@5.4.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA=="],
|
||||
@@ -1707,6 +1720,8 @@
|
||||
|
||||
"@babel/traverse--for-generate-function-map/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
|
||||
|
||||
"@callstack/react-theme-provider/deepmerge": ["deepmerge@3.3.0", "", {}, "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||
@@ -1887,6 +1902,8 @@
|
||||
|
||||
"react-native/semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"react-native-paper/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
|
||||
|
||||
"react-native-vector-icons/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
|
||||
|
||||
"react-native-web/@react-native/normalize-colors": ["@react-native/normalize-colors@0.74.89", "", {}, "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg=="],
|
||||
@@ -2007,6 +2024,8 @@
|
||||
|
||||
"ora/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
||||
|
||||
"react-native-paper/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"react-native-vector-icons/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="],
|
||||
|
||||
"react-native-vector-icons/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="],
|
||||
@@ -2043,6 +2062,8 @@
|
||||
|
||||
"ora/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
|
||||
|
||||
"react-native-paper/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"react-native-vector-icons/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
@@ -95,12 +95,12 @@ const styles = StyleSheet.create({
|
||||
fontSize: TEXT_SIZE_LARGE,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 20,
|
||||
color: MainColor.white,
|
||||
color: MainColor.white_gray,
|
||||
},
|
||||
alertMessage: {
|
||||
textAlign: "center",
|
||||
marginBottom: 20,
|
||||
color: MainColor.white,
|
||||
color: MainColor.white_gray,
|
||||
},
|
||||
alertButtons: {
|
||||
flexDirection: "row",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { PADDING_EXTRA_SMALL, PADDING_MEDIUM, PADDING_SMALL } from "@/constants/constans-value";
|
||||
import { StyleProp, TouchableHighlight, View, ViewStyle } from "react-native";
|
||||
|
||||
interface BaseBoxProps {
|
||||
@@ -8,14 +9,16 @@ interface BaseBoxProps {
|
||||
marginBottom?: number;
|
||||
padding?: number;
|
||||
paddingInline?: number;
|
||||
paddingBlock?: number;
|
||||
}
|
||||
|
||||
export default function BaseBox({
|
||||
children,
|
||||
style,
|
||||
onPress,
|
||||
marginBottom = 16,
|
||||
padding = 12,
|
||||
marginBottom = PADDING_MEDIUM,
|
||||
paddingBlock = PADDING_EXTRA_SMALL,
|
||||
paddingInline = PADDING_SMALL,
|
||||
}: BaseBoxProps) {
|
||||
return (
|
||||
<>
|
||||
@@ -29,7 +32,8 @@ export default function BaseBox({
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
marginBottom,
|
||||
padding,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
@@ -46,7 +50,8 @@ export default function BaseBox({
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
marginBottom,
|
||||
padding,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
|
||||
16
components/Box/BoxButtonOnFooter.tsx
Normal file
16
components/Box/BoxButtonOnFooter.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { StyleProp, View, ViewStyle } from "react-native";
|
||||
|
||||
export default function BoxButtonOnFooter({
|
||||
children,
|
||||
style,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}) {
|
||||
return (
|
||||
<View style={GStyles.bottomBar}>
|
||||
<View style={[GStyles.bottomBarContainer, style]}>{children}</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export default function InformationBox({ text }: { text: string }) {
|
||||
<Ionicons
|
||||
name="information-circle-outline"
|
||||
size={24}
|
||||
color={MainColor.white}
|
||||
color={MainColor.white_gray}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={10} style={{ justifyContent: "center" }}>
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { router } from "expo-router";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Href, router } from "expo-router";
|
||||
|
||||
const BackButton = () => {
|
||||
/**
|
||||
*
|
||||
* @param path - path to navigate to ?
|
||||
* @default router.back()
|
||||
* @returns if path : router.replace(path) else router.back()
|
||||
*/
|
||||
const LeftButtonCustom = ({
|
||||
path,
|
||||
icon = "arrow-back",
|
||||
iconCustom,
|
||||
}: {
|
||||
path?: Href;
|
||||
icon?: React.ReactNode | any;
|
||||
iconCustom?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.back()}
|
||||
/>
|
||||
<>
|
||||
{iconCustom ? (
|
||||
iconCustom
|
||||
) : (
|
||||
<Ionicons
|
||||
name={icon}
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => (path ? router.replace(path) : router.back())}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackButton;
|
||||
export default LeftButtonCustom;
|
||||
|
||||
29
components/Button/ButtonCenteredOnly.tsx
Normal file
29
components/Button/ButtonCenteredOnly.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import React from "react";
|
||||
import ButtonCustom from "./ButtonCustom";
|
||||
|
||||
interface ButtonCenteredOnlyProps {
|
||||
children?: React.ReactNode;
|
||||
icon?: "plus" | "upload";
|
||||
onPress: () => void;
|
||||
}
|
||||
export default function ButtonCenteredOnly({
|
||||
onPress,
|
||||
children,
|
||||
icon = "plus"
|
||||
}: ButtonCenteredOnlyProps) {
|
||||
return (
|
||||
<ButtonCustom
|
||||
onPress={onPress}
|
||||
iconLeft={
|
||||
<Feather name={icon} size={ICON_SIZE_BUTTON} color={MainColor.black} />
|
||||
}
|
||||
style={[GStyles.buttonCentered50Percent]}
|
||||
>
|
||||
{children}
|
||||
</ButtonCustom>
|
||||
);
|
||||
}
|
||||
@@ -2,18 +2,19 @@
|
||||
|
||||
import React from "react";
|
||||
import { StyleProp, Text, TouchableOpacity, ViewStyle } from "react-native";
|
||||
import buttonStyles from "./buttonCustomStyles";
|
||||
import { radiusMap } from "@/constants/radius-value";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { stylesButton } from "./buttonCustomStyles";
|
||||
import { Href, router } from "expo-router";
|
||||
|
||||
// Import radiusMap
|
||||
|
||||
|
||||
// Definisi type untuk radius
|
||||
type RadiusType = keyof typeof radiusMap | number;
|
||||
|
||||
interface ButtonProps {
|
||||
children?: React.ReactNode;
|
||||
href?: Href;
|
||||
onPress?: () => void;
|
||||
title?: string;
|
||||
backgroundColor?: string;
|
||||
@@ -26,34 +27,41 @@ interface ButtonProps {
|
||||
|
||||
const ButtonCustom: React.FC<ButtonProps> = ({
|
||||
children,
|
||||
href,
|
||||
onPress,
|
||||
title = "Button",
|
||||
backgroundColor = MainColor.yellow,
|
||||
textColor = MainColor.black,
|
||||
radius = "full", // default md
|
||||
radius = 50, // default md
|
||||
disabled = false,
|
||||
iconLeft,
|
||||
style,
|
||||
}) => {
|
||||
const borderRadius =
|
||||
typeof radius === "number" ? radius : radiusMap[radius ?? "md"]; // fallback ke 'md'
|
||||
|
||||
const styles = buttonStyles({
|
||||
backgroundColor,
|
||||
textColor,
|
||||
borderRadius,
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.button, disabled && styles.disabled, style]}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
stylesButton.button,
|
||||
{ borderRadius: radius },
|
||||
disabled
|
||||
? [stylesButton.disabled, { backgroundColor: MainColor.disabled }]
|
||||
: { backgroundColor },
|
||||
style,
|
||||
]}
|
||||
onPress={() => {
|
||||
if (href) {
|
||||
router.push(href);
|
||||
} else {
|
||||
onPress?.();
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{/* Render icon jika tersedia */}
|
||||
{iconLeft && iconLeft}
|
||||
<Text style={styles.buttonText}>{children || title}</Text>
|
||||
<Text style={[stylesButton.buttonText, { color: textColor }]}>
|
||||
{children || title}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import React from "react";
|
||||
import ButtonCustom from "./ButtonCustom";
|
||||
|
||||
interface ButtonUploadProps {
|
||||
title?: string;
|
||||
onPress: () => void;
|
||||
}
|
||||
export default function ButtonUpload({ onPress, title = "Upload" }: ButtonUploadProps) {
|
||||
return (
|
||||
<ButtonCustom
|
||||
onPress={onPress}
|
||||
iconLeft={<Feather name="upload" size={20} color={MainColor.black} />}
|
||||
style={GStyles.buttonCentered50Percent}
|
||||
>
|
||||
{title}
|
||||
</ButtonCustom>
|
||||
);
|
||||
}
|
||||
45
components/Button/FloatingButton.tsx
Normal file
45
components/Button/FloatingButton.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
// components/FloatingButton.tsx
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import React from "react";
|
||||
import { StyleSheet, ViewStyle } from "react-native";
|
||||
import { FAB } from "react-native-paper";
|
||||
|
||||
// Props untuk komponen
|
||||
interface FloatingButtonProps {
|
||||
onPress: () => void;
|
||||
// label?: string;
|
||||
icon?: string; // MaterialCommunityIcons
|
||||
style?: ViewStyle;
|
||||
}
|
||||
|
||||
const FloatingButton: React.FC<FloatingButtonProps> = ({
|
||||
onPress,
|
||||
// label = "Buat",
|
||||
icon = "pencil-plus-outline",
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<FAB
|
||||
style={[styles.fab, style]}
|
||||
icon={icon}
|
||||
color={MainColor.white}
|
||||
// label={label}
|
||||
onPress={onPress}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fab: {
|
||||
position: "absolute",
|
||||
margin: 16,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: AccentColor.softblue, // Warna Twitter biru
|
||||
borderRadius: 50,
|
||||
borderColor: AccentColor.blue,
|
||||
borderWidth: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default FloatingButton;
|
||||
@@ -1,31 +1,25 @@
|
||||
// components/Button/buttonStyles.js
|
||||
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { TEXT_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
export default function buttonStyles({
|
||||
backgroundColor = "#007AFF",
|
||||
textColor = "#FFFFFF",
|
||||
borderRadius = 8,
|
||||
}) {
|
||||
return StyleSheet.create({
|
||||
export const stylesButton = StyleSheet.create({
|
||||
button: {
|
||||
backgroundColor,
|
||||
paddingVertical: 12,
|
||||
backgroundColor: MainColor.yellow,
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 20,
|
||||
borderRadius,
|
||||
flexDirection: "row", // 👈 Tambahkan baris ini
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
},
|
||||
buttonText: {
|
||||
color: textColor,
|
||||
fontSize: 16,
|
||||
color: MainColor.black,
|
||||
fontSize: TEXT_SIZE_MEDIUM,
|
||||
fontWeight: "600",
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: MainColor.disabled,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
51
components/Center/CenterCustom.tsx
Normal file
51
components/Center/CenterCustom.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
// Center.tsx
|
||||
import React from "react";
|
||||
import { View, StyleSheet, ViewStyle } from "react-native";
|
||||
|
||||
type JustifyContent =
|
||||
| "flex-start"
|
||||
| "flex-end"
|
||||
| "center"
|
||||
| "space-between"
|
||||
| "space-around"
|
||||
| "space-evenly";
|
||||
|
||||
type AlignItems = "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
||||
|
||||
interface CenterProps {
|
||||
children: React.ReactNode;
|
||||
style?: ViewStyle;
|
||||
direction?: "row" | "column";
|
||||
justifyContent?: JustifyContent;
|
||||
alignItems?: AlignItems;
|
||||
}
|
||||
|
||||
const CenterCustom: React.FC<CenterProps> = ({
|
||||
children,
|
||||
style,
|
||||
direction = "column",
|
||||
justifyContent = "center",
|
||||
alignItems = "center",
|
||||
}) => {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{ flexDirection: direction },
|
||||
{ justifyContent },
|
||||
{ alignItems },
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default CenterCustom;
|
||||
36
components/Clickable/ClickableCustom.tsx
Normal file
36
components/Clickable/ClickableCustom.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { StyleSheet, TouchableOpacity } from "react-native";
|
||||
|
||||
export default function ClickableCustom({
|
||||
children,
|
||||
onPress,
|
||||
disabled,
|
||||
style,
|
||||
...props
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
onPress: () => void;
|
||||
disabled?: boolean;
|
||||
style?: any;
|
||||
[key: string]: any;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
style={[styles.container, style]}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
},
|
||||
});
|
||||
186
components/Divider/DividerCustom.tsx
Normal file
186
components/Divider/DividerCustom.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import React from "react";
|
||||
import {
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
|
||||
// Define types for props
|
||||
type Orientation = "horizontal" | "vertical";
|
||||
type HorizontalLabelPosition = "center" | "left" | "right";
|
||||
type VerticalLabelPosition = "center" | "top" | "bottom";
|
||||
type LabelPosition = HorizontalLabelPosition | VerticalLabelPosition;
|
||||
|
||||
type Size = number | "xs" | "sm" | "md" | "lg" | "xl";
|
||||
|
||||
interface DividerProps {
|
||||
orientation?: Orientation;
|
||||
color?: string;
|
||||
size?: Size;
|
||||
label?: React.ReactNode;
|
||||
labelPosition?: LabelPosition;
|
||||
labelStyle?: StyleProp<TextStyle>;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const DividerCustom: React.FC<DividerProps> = ({
|
||||
orientation = "horizontal",
|
||||
color = "#DADADA",
|
||||
size = "xs",
|
||||
label,
|
||||
labelPosition = "center",
|
||||
labelStyle,
|
||||
style,
|
||||
}) => {
|
||||
const isHorizontal = orientation === "horizontal";
|
||||
|
||||
// Convert size to actual dimensions
|
||||
const getSize = (): number => {
|
||||
if (typeof size === "number") return size;
|
||||
|
||||
switch (size) {
|
||||
case "xs":
|
||||
return 1;
|
||||
case "sm":
|
||||
return 2;
|
||||
case "md":
|
||||
return 3;
|
||||
case "lg":
|
||||
return 4;
|
||||
case "xl":
|
||||
return 5;
|
||||
default:
|
||||
return 1; // Default size
|
||||
}
|
||||
};
|
||||
|
||||
const thickness = getSize();
|
||||
|
||||
const capitalize = (str: string): string =>
|
||||
str.charAt(0).toUpperCase() + str.slice(1);
|
||||
|
||||
const renderLabel = () => {
|
||||
if (!label) return null;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.labelContainer,
|
||||
isHorizontal
|
||||
? styles[
|
||||
`label${capitalize(
|
||||
labelPosition as string
|
||||
)}` as keyof typeof styles
|
||||
]
|
||||
: styles[
|
||||
`label${capitalize(
|
||||
labelPosition as string
|
||||
)}` as keyof typeof styles
|
||||
],
|
||||
]}
|
||||
>
|
||||
<Text style={[styles.labelText, labelStyle]}>{label}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
isHorizontal ? styles.horizontalDivider : styles.verticalDivider,
|
||||
{ backgroundColor: color },
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{isHorizontal ? (
|
||||
labelPosition !== "center" ? (
|
||||
<View style={{ flex: 1, flexDirection: "row", alignItems: "center" }}>
|
||||
{labelPosition === "left" && renderLabel()}
|
||||
<View
|
||||
style={{ flex: 1, backgroundColor: color, height: thickness }}
|
||||
/>
|
||||
{labelPosition === "right" && renderLabel()}
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{renderLabel()}
|
||||
<View style={{ flex: 1 }} />
|
||||
</>
|
||||
)
|
||||
) : labelPosition !== "center" && !isHorizontal ? (
|
||||
<View
|
||||
style={{ flex: 1, flexDirection: "column", justifyContent: "center" }}
|
||||
>
|
||||
{labelPosition === "top" && renderLabel()}
|
||||
<View style={{ flex: 1, backgroundColor: color, width: thickness }} />
|
||||
{labelPosition === "bottom" && renderLabel()}
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{renderLabel()}
|
||||
<View style={{ flex: 1 }} />
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
horizontalDivider: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginVertical: 8,
|
||||
position: "relative",
|
||||
},
|
||||
verticalDivider: {
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
marginHorizontal: 8,
|
||||
position: "relative",
|
||||
},
|
||||
labelText: {
|
||||
fontSize: 14,
|
||||
fontWeight: "500",
|
||||
color: "#666",
|
||||
marginHorizontal: 12,
|
||||
marginVertical: 4,
|
||||
backgroundColor: "white",
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
labelContainer: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
labelLeft: {
|
||||
left: 12,
|
||||
right: null,
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
labelRight: {
|
||||
right: 12,
|
||||
left: null,
|
||||
alignItems: "flex-end",
|
||||
},
|
||||
labelCenter: {
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
labelTop: {
|
||||
top: 12,
|
||||
bottom: null,
|
||||
justifyContent: "flex-start",
|
||||
},
|
||||
labelBottom: {
|
||||
bottom: 12,
|
||||
top: null,
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
});
|
||||
|
||||
export default DividerCustom;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from "react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import {
|
||||
Animated,
|
||||
PanResponder,
|
||||
@@ -14,7 +14,7 @@ interface DrawerCustomProps {
|
||||
children?: React.ReactNode;
|
||||
height?: number;
|
||||
isVisible: boolean;
|
||||
drawerAnim: Animated.Value;
|
||||
drawerAnim?: Animated.Value;
|
||||
closeDrawer: () => void;
|
||||
// openLogoutAlert: () => void;
|
||||
}
|
||||
@@ -33,6 +33,26 @@ export default function DrawerCustom({
|
||||
closeDrawer,
|
||||
}: // openLogoutAlert,
|
||||
DrawerCustomProps) {
|
||||
const drawerAnima = useRef(
|
||||
new Animated.Value(height || DRAWER_HEIGHT)
|
||||
).current;
|
||||
// Efek untuk handle open/close drawer
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
Animated.timing(drawerAnima, {
|
||||
toValue: 0,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
} else {
|
||||
Animated.timing(drawerAnima, {
|
||||
toValue: height || DRAWER_HEIGHT,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}
|
||||
}, [isVisible, drawerAnim, height, closeDrawer, drawerAnima]);
|
||||
|
||||
const panResponder = useRef(
|
||||
PanResponder.create({
|
||||
onMoveShouldSetPanResponder: (_, gestureState) => {
|
||||
@@ -41,7 +61,7 @@ DrawerCustomProps) {
|
||||
onPanResponderMove: (_, gestureState) => {
|
||||
const offset = gestureState.dy;
|
||||
if (offset >= 0 && offset <= DRAWER_HEIGHT) {
|
||||
drawerAnim.setValue(offset);
|
||||
drawerAnima.setValue(offset);
|
||||
}
|
||||
},
|
||||
onPanResponderRelease: (_, gestureState) => {
|
||||
@@ -50,7 +70,7 @@ DrawerCustomProps) {
|
||||
closeDrawer();
|
||||
});
|
||||
} else {
|
||||
Animated.spring(drawerAnim, {
|
||||
Animated.spring(drawerAnima, {
|
||||
toValue: 0,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
@@ -80,7 +100,7 @@ DrawerCustomProps) {
|
||||
styles.drawer,
|
||||
{
|
||||
height: height || DRAWER_HEIGHT,
|
||||
transform: [{ translateY: drawerAnim }],
|
||||
transform: [{ translateY: drawerAnima }],
|
||||
},
|
||||
]}
|
||||
{...panResponder.panHandlers}
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM, TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
|
||||
import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||
import { IMenuDrawerItem } from "../_Interface/types";
|
||||
|
||||
const MenuDrawerDynamicGrid = ({ data, columns = 3, onPressItem }: any) => {
|
||||
const numColumns = columns;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{data.map((item: any, index: any) => (
|
||||
{data.map((item: IMenuDrawerItem, index: any) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={[styles.itemContainer, { flexBasis: `${100 / numColumns}%` }]}
|
||||
onPress={() => onPressItem?.(item)}
|
||||
>
|
||||
<View style={styles.iconContainer}>
|
||||
<Ionicons
|
||||
<View
|
||||
style={[
|
||||
styles.iconContainer,
|
||||
{ backgroundColor: item.color || AccentColor.blue },
|
||||
]}
|
||||
>
|
||||
{item.icon}
|
||||
{/* <Ionicons
|
||||
name={item.icon}
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={item.color || MainColor.white}
|
||||
/>
|
||||
color={item.color || MainColor.white_gray}
|
||||
/> */}
|
||||
</View>
|
||||
<Text style={styles.label}>{item.label}</Text>
|
||||
<Text
|
||||
style={[styles.label, { color: item.color || AccentColor.white }]}
|
||||
>
|
||||
{item.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
@@ -52,6 +62,6 @@ const styles = StyleSheet.create({
|
||||
marginTop: 10,
|
||||
fontSize: TEXT_SIZE_SMALL,
|
||||
textAlign: "center",
|
||||
color: MainColor.white,
|
||||
color: MainColor.white_gray,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,5 +109,6 @@ const styles = StyleSheet.create({
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "flex-start",
|
||||
marginInline: 0.1
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image, ImageSourcePropType, StyleSheet } from "react-native";
|
||||
import { Href, router } from "expo-router";
|
||||
import {
|
||||
Image,
|
||||
ImageSourcePropType,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
} from "react-native";
|
||||
|
||||
type Size = "base" | "sm" | "md" | "lg" | "xl";
|
||||
|
||||
interface AvatarCustomProps {
|
||||
source?: ImageSourcePropType;
|
||||
size?: Size;
|
||||
onPress?: () => void;
|
||||
href?: Href | undefined;
|
||||
}
|
||||
|
||||
const sizeMap = {
|
||||
@@ -20,24 +29,42 @@ const sizeMap = {
|
||||
export default function AvatarCustom({
|
||||
source = DUMMY_IMAGE.avatar,
|
||||
size = "base",
|
||||
onPress,
|
||||
href,
|
||||
}: AvatarCustomProps) {
|
||||
const dimension = sizeMap[size];
|
||||
|
||||
const ImageView = ({source}: {source: ImageSourcePropType}) => {
|
||||
return (
|
||||
<Image
|
||||
source={source}
|
||||
style={[
|
||||
// styles.overlappingAvatar,
|
||||
{
|
||||
width: dimension,
|
||||
height: dimension,
|
||||
borderRadius: dimension / 2,
|
||||
},
|
||||
]}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Image
|
||||
source={source}
|
||||
style={[
|
||||
styles.overlappingAvatar,
|
||||
{
|
||||
width: dimension,
|
||||
height: dimension,
|
||||
borderRadius: dimension / 2,
|
||||
},
|
||||
]}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
<>
|
||||
{onPress || href ? (
|
||||
<TouchableOpacity
|
||||
onPress={href ? () => router.navigate(href as any) : onPress}
|
||||
>
|
||||
<ImageView source={source} />
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<ImageView source={source} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
@@ -47,7 +74,7 @@ const styles = StyleSheet.create({
|
||||
overlappingAvatar: {
|
||||
borderWidth: 2,
|
||||
borderColor: "#fff",
|
||||
backgroundColor: MainColor.white,
|
||||
backgroundColor: MainColor.white_gray,
|
||||
// shadowColor: "#000",
|
||||
// shadowOffset: { width: 0, height: 2 },
|
||||
// shadowOpacity: 0.2,
|
||||
|
||||
61
components/Map/MapCustom.tsx
Normal file
61
components/Map/MapCustom.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
// components/MapComponent.js
|
||||
|
||||
import React from "react";
|
||||
import { DimensionValue, StyleSheet, View } from "react-native";
|
||||
import MapView, { Marker } from "react-native-maps";
|
||||
|
||||
interface MapComponentProps {
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
latitudeDelta?: number;
|
||||
longitudeDelta?: number;
|
||||
height?: DimensionValue;
|
||||
}
|
||||
|
||||
const MapCustom = ({
|
||||
latitude = -8.737109,
|
||||
longitude = 115.1756897,
|
||||
latitudeDelta = 0.1,
|
||||
longitudeDelta = 0.1,
|
||||
height = 300,
|
||||
}: MapComponentProps) => {
|
||||
const initialRegion = {
|
||||
latitude,
|
||||
longitude, // Jakarta sebagai default lokasi
|
||||
latitudeDelta,
|
||||
longitudeDelta,
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<MapView
|
||||
style={[styles.map, { height }]}
|
||||
initialRegion={initialRegion}
|
||||
showsUserLocation={true}
|
||||
loadingEnabled={true}
|
||||
>
|
||||
{/* Contoh marker */}
|
||||
<Marker
|
||||
coordinate={{
|
||||
latitude,
|
||||
longitude,
|
||||
}}
|
||||
title="Bali"
|
||||
description="Badung, Bali, Indonesia"
|
||||
/>
|
||||
</MapView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
borderRadius: 8,
|
||||
},
|
||||
map: {
|
||||
width: "100%",
|
||||
},
|
||||
});
|
||||
|
||||
export default MapCustom;
|
||||
60
components/Scroll/ScrollCustom.tsx
Normal file
60
components/Scroll/ScrollCustom.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import React from "react";
|
||||
import { ScrollView, StyleSheet } from "react-native";
|
||||
import ButtonCustom from "../Button/ButtonCustom";
|
||||
|
||||
interface ButtonData {
|
||||
id: string | number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface ScrollableCustomProps {
|
||||
data: ButtonData[];
|
||||
onButtonPress: (item: ButtonData) => void;
|
||||
activeId?: string | number;
|
||||
}
|
||||
|
||||
const ScrollableCustom = ({
|
||||
data,
|
||||
onButtonPress,
|
||||
activeId,
|
||||
}: ScrollableCustomProps) => {
|
||||
return (
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.buttonContainer}
|
||||
style={styles.scrollView}
|
||||
>
|
||||
{data.map((item) => {
|
||||
const isActive = activeId === item.id;
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
key={item.id}
|
||||
backgroundColor={isActive ? MainColor.yellow : AccentColor.blue}
|
||||
textColor={isActive ? MainColor.black : MainColor.white}
|
||||
onPress={() => onButtonPress(item)}
|
||||
>
|
||||
{item.label}
|
||||
</ButtonCustom>
|
||||
);
|
||||
})}
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScrollableCustom;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollView: {
|
||||
// maxHeight: 50,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
// paddingHorizontal: 16,
|
||||
// paddingVertical: 10,
|
||||
gap: 12,
|
||||
},
|
||||
});
|
||||
@@ -1,15 +1,13 @@
|
||||
// components/Select.tsx
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { TEXT_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
Pressable,
|
||||
Modal,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
Modal,
|
||||
Pressable,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
type SelectItem = {
|
||||
@@ -23,7 +21,9 @@ type SelectProps = {
|
||||
data: SelectItem[];
|
||||
value?: string | number | null;
|
||||
required?: boolean; // <-- new prop
|
||||
disabled?: boolean; // <-- tambahkan prop disabled
|
||||
onChange: (value: string | number) => void;
|
||||
borderRadius?: number;
|
||||
};
|
||||
|
||||
const SelectCustom: React.FC<SelectProps> = ({
|
||||
@@ -32,7 +32,9 @@ const SelectCustom: React.FC<SelectProps> = ({
|
||||
data,
|
||||
value,
|
||||
required = false, // <-- default false
|
||||
disabled = false, // <-- default false
|
||||
onChange,
|
||||
borderRadius = 8,
|
||||
}) => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
|
||||
@@ -41,35 +43,50 @@ const SelectCustom: React.FC<SelectProps> = ({
|
||||
const hasError = required && value === null; // <-- check if empty and required
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={GStyles.inputContainerArea}>
|
||||
{label && (
|
||||
<Text style={styles.label}>
|
||||
<Text style={GStyles.inputLabel}>
|
||||
{label}
|
||||
{required && <Text style={styles.requiredIndicator}> *</Text>}
|
||||
{required && <Text style={GStyles.inputRequired}> *</Text>}
|
||||
</Text>
|
||||
)}
|
||||
<Pressable
|
||||
style={[styles.input, hasError ? styles.inputError : null]} // <-- add error style
|
||||
onPress={() => setModalVisible(true)}
|
||||
style={[
|
||||
{ borderRadius },
|
||||
hasError ? GStyles.inputErrorBorder : null,
|
||||
GStyles.inputContainerInput,
|
||||
disabled && GStyles.disabledBox,
|
||||
]} // <-- add error style
|
||||
onPress={() => !disabled && setModalVisible(true)}
|
||||
>
|
||||
<Text style={selectedItem ? styles.text : styles.placeholder}>
|
||||
<Text
|
||||
style={
|
||||
selectedItem
|
||||
? disabled
|
||||
? GStyles.inputTextDisabled
|
||||
: GStyles.inputText
|
||||
: disabled
|
||||
? GStyles.inputPlaceholderDisabled
|
||||
: GStyles.inputPlaceholder
|
||||
}
|
||||
>
|
||||
{selectedItem?.label || placeholder}
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
||||
<Modal visible={modalVisible} transparent animationType="fade">
|
||||
<TouchableOpacity
|
||||
style={styles.modalOverlay}
|
||||
style={GStyles.selectModalOverlay}
|
||||
activeOpacity={1}
|
||||
onPressOut={() => setModalVisible(false)}
|
||||
>
|
||||
<View style={styles.modalContent}>
|
||||
<View style={GStyles.selectModalContent}>
|
||||
<FlatList
|
||||
data={data}
|
||||
keyExtractor={(item) => String(item.value)}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity
|
||||
style={styles.option}
|
||||
style={GStyles.selectOption}
|
||||
onPress={() => {
|
||||
onChange(item.value);
|
||||
setModalVisible(false);
|
||||
@@ -85,68 +102,10 @@ const SelectCustom: React.FC<SelectProps> = ({
|
||||
|
||||
{/* Optional Error Message */}
|
||||
{hasError && (
|
||||
<Text style={styles.errorMessage}>Harap pilih salah satu</Text>
|
||||
<Text style={GStyles.inputErrorMessage}>Harap pilih salah satu</Text>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectCustom;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
label: {
|
||||
fontSize: TEXT_SIZE_MEDIUM,
|
||||
marginBottom: 4,
|
||||
color: MainColor.white,
|
||||
fontWeight: "500",
|
||||
},
|
||||
requiredIndicator: {
|
||||
color: "red",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: "#ccc",
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
minHeight: 48,
|
||||
justifyContent: "center",
|
||||
backgroundColor: MainColor.white,
|
||||
},
|
||||
inputError: {
|
||||
borderColor: "red",
|
||||
},
|
||||
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",
|
||||
},
|
||||
errorMessage: {
|
||||
marginTop: 4,
|
||||
fontSize: 12,
|
||||
color: "red",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// components/Stack.tsx
|
||||
|
||||
import React from "react";
|
||||
@@ -57,10 +58,10 @@ const convertToSpacing = (value: GapSizeType): number => {
|
||||
return sizes[value] || 16; // default md
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
stack: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
// const styles = StyleSheet.create({
|
||||
// stack: {
|
||||
// flex: 1,
|
||||
// },
|
||||
// });
|
||||
|
||||
export default StackCustom;
|
||||
|
||||
@@ -5,7 +5,13 @@ import {
|
||||
TEXT_SIZE_SMALL,
|
||||
} from "@/constants/constans-value";
|
||||
import React from "react";
|
||||
import { Text as RNText, StyleProp, StyleSheet, TextStyle } from "react-native";
|
||||
import {
|
||||
Text as RNText,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
TextStyle,
|
||||
TouchableOpacity,
|
||||
} from "react-native";
|
||||
|
||||
// Tambahkan type TextAlignProps agar lebih type-safe
|
||||
type TextAlign = "left" | "center" | "right";
|
||||
@@ -16,9 +22,10 @@ interface TextCustomProps {
|
||||
bold?: boolean;
|
||||
semiBold?: boolean;
|
||||
size?: "default" | "large" | "small";
|
||||
color?: "default" | "yellow" | "red";
|
||||
color?: "default" | "yellow" | "red" | "gray" | "green";
|
||||
align?: TextAlign; // Prop untuk alignment
|
||||
truncate?: boolean | number;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
const TextCustom: React.FC<TextCustomProps> = ({
|
||||
@@ -30,6 +37,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
||||
color = "default",
|
||||
align = "left", // Default alignment
|
||||
truncate = false,
|
||||
onPress,
|
||||
}) => {
|
||||
const getStyle = () => {
|
||||
let selectedStyles = [];
|
||||
@@ -48,6 +56,8 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
||||
// Color
|
||||
if (color === "yellow") selectedStyles.push(styles.yellow);
|
||||
else if (color === "red") selectedStyles.push(styles.red);
|
||||
else if (color === "gray") selectedStyles.push(styles.gray);
|
||||
else if (color === "green") selectedStyles.push(styles.green);
|
||||
|
||||
// Alignment
|
||||
if (align) {
|
||||
@@ -60,7 +70,19 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
||||
return selectedStyles;
|
||||
};
|
||||
|
||||
return (
|
||||
return onPress ? (
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
<RNText
|
||||
numberOfLines={
|
||||
typeof truncate === "number" ? truncate : truncate ? 1 : undefined
|
||||
}
|
||||
ellipsizeMode="tail"
|
||||
style={getStyle()}
|
||||
>
|
||||
{children}
|
||||
</RNText>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<RNText
|
||||
numberOfLines={
|
||||
typeof truncate === "number" ? truncate : truncate ? 1 : undefined
|
||||
@@ -80,6 +102,7 @@ export const styles = StyleSheet.create({
|
||||
fontSize: TEXT_SIZE_MEDIUM,
|
||||
color: MainColor.white,
|
||||
fontFamily: "Poppins-Regular",
|
||||
lineHeight: 20,
|
||||
},
|
||||
bold: {
|
||||
fontFamily: "Poppins-Bold",
|
||||
@@ -101,4 +124,10 @@ export const styles = StyleSheet.create({
|
||||
red: {
|
||||
color: MainColor.red,
|
||||
},
|
||||
gray: {
|
||||
color: MainColor.placeholder,
|
||||
},
|
||||
green: {
|
||||
color: MainColor.green,
|
||||
},
|
||||
});
|
||||
|
||||
146
components/TextArea/TextAreaCustom.tsx
Normal file
146
components/TextArea/TextAreaCustom.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
TextInput as RNTextInput,
|
||||
StyleProp,
|
||||
Text,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
|
||||
type IconType = React.ReactNode | string;
|
||||
|
||||
type BaseProps = {
|
||||
iconLeft?: IconType;
|
||||
iconRight?: IconType;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
error?: string;
|
||||
fontColor?: string;
|
||||
disabled?: boolean;
|
||||
borderRadius?: number;
|
||||
autosize?: boolean;
|
||||
minRows?: number;
|
||||
maxRows?: number;
|
||||
showCount?: boolean;
|
||||
maxLength?: number;
|
||||
height?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
type NativeTextInputProps = Omit<
|
||||
React.ComponentProps<typeof RNTextInput>,
|
||||
"style"
|
||||
>;
|
||||
|
||||
export type TextAreaCustomProps = BaseProps & NativeTextInputProps;
|
||||
|
||||
const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
|
||||
iconLeft,
|
||||
iconRight,
|
||||
label,
|
||||
required = false,
|
||||
error = "",
|
||||
fontColor = "#000",
|
||||
disabled = false,
|
||||
borderRadius = 8,
|
||||
autosize = false,
|
||||
minRows = 4,
|
||||
maxRows = 6,
|
||||
showCount = false,
|
||||
maxLength,
|
||||
value,
|
||||
onChangeText,
|
||||
height = 100,
|
||||
style,
|
||||
...rest
|
||||
}) => {
|
||||
const [numberOfLines, setNumberOfLines] = useState(minRows);
|
||||
|
||||
// Autosizing logic
|
||||
useEffect(() => {
|
||||
if (!autosize || !value) return;
|
||||
|
||||
const text = value as string;
|
||||
const lines = text.split("\n").length;
|
||||
const newLines = Math.max(minRows, Math.min(maxRows, lines));
|
||||
setNumberOfLines(newLines);
|
||||
}, [value, autosize, minRows, maxRows]);
|
||||
|
||||
const hasError = Boolean(error);
|
||||
|
||||
const renderIcon = (icon: IconType) => {
|
||||
if (!icon) return null;
|
||||
return typeof icon === "string" ? (
|
||||
<Text style={GStyles.inputIconText}>{icon}</Text>
|
||||
) : (
|
||||
icon
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={GStyles.inputContainerArea}>
|
||||
{label && (
|
||||
<Text style={GStyles.inputLabel}>
|
||||
{label}
|
||||
{required && <Text style={GStyles.inputRequired}> *</Text>}
|
||||
</Text>
|
||||
)}
|
||||
<View
|
||||
style={[
|
||||
GStyles.inputContainerInput,
|
||||
disabled && GStyles.disabledBox,
|
||||
hasError ? GStyles.inputErrorBorder : {},
|
||||
{ borderRadius },
|
||||
{ height },
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{iconLeft && (
|
||||
<View style={GStyles.inputIcon}>{renderIcon(iconLeft)}</View>
|
||||
)}
|
||||
|
||||
<RNTextInput
|
||||
maxLength={maxLength}
|
||||
multiline
|
||||
numberOfLines={numberOfLines}
|
||||
style={[
|
||||
GStyles.inputText,
|
||||
GStyles.textAreaInput,
|
||||
{ color: fontColor },
|
||||
]}
|
||||
editable={!disabled}
|
||||
value={value as string}
|
||||
onChangeText={onChangeText}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
{iconRight && (
|
||||
<View style={GStyles.inputIcon}>{renderIcon(iconRight)}</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Error Message atau Counter */}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: 4,
|
||||
minHeight: 16,
|
||||
}}
|
||||
>
|
||||
{hasError ? (
|
||||
<Text style={GStyles.inputErrorMessage}>{error}</Text>
|
||||
) : null}
|
||||
|
||||
{showCount && maxLength ? (
|
||||
<Text style={GStyles.inputMaxLength}>
|
||||
{(value as string)?.length || 0}/{maxLength}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextAreaCustom;
|
||||
@@ -1,3 +1,4 @@
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
View,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
import { textInputStyles } from "./textInputStyles";
|
||||
|
||||
type IconType = React.ReactNode | string;
|
||||
|
||||
@@ -23,9 +23,11 @@ type Props = {
|
||||
disabled?: boolean;
|
||||
borderRadius?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
maxLength?: number;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
} & Omit<React.ComponentProps<typeof RNTextInput>, "style">;
|
||||
|
||||
export const TextInputCustom = ({
|
||||
const TextInputCustom = ({
|
||||
iconLeft,
|
||||
iconRight,
|
||||
label,
|
||||
@@ -38,6 +40,8 @@ export const TextInputCustom = ({
|
||||
style,
|
||||
keyboardType,
|
||||
onChangeText,
|
||||
maxLength,
|
||||
containerStyle,
|
||||
...rest
|
||||
}: Props) => {
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
@@ -47,7 +51,7 @@ export const TextInputCustom = ({
|
||||
const renderIcon = (icon: IconType) => {
|
||||
if (!icon) return null;
|
||||
return typeof icon === "string" ? (
|
||||
<Text style={textInputStyles.iconText}>{icon}</Text>
|
||||
<Text style={GStyles.inputIconText}>{icon}</Text>
|
||||
) : (
|
||||
icon
|
||||
);
|
||||
@@ -71,37 +75,42 @@ export const TextInputCustom = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={textInputStyles.container}>
|
||||
<View style={[GStyles.inputContainerArea, containerStyle]}>
|
||||
{label && (
|
||||
<Text style={textInputStyles.label}>
|
||||
<Text style={GStyles.inputLabel}>
|
||||
{label}
|
||||
{required && <Text style={textInputStyles.required}> *</Text>}
|
||||
{required && <Text style={GStyles.inputRequired}> *</Text>}
|
||||
</Text>
|
||||
)}
|
||||
<View
|
||||
style={[
|
||||
textInputStyles.inputContainer,
|
||||
disabled && textInputStyles.disabled,
|
||||
{ borderRadius },
|
||||
externalError || internalError ? textInputStyles.errorBorder : null,
|
||||
style,
|
||||
{ borderRadius },
|
||||
externalError || internalError ? GStyles.inputErrorBorder : null,
|
||||
GStyles.inputContainerInput,
|
||||
disabled && GStyles.disabledBox,
|
||||
]}
|
||||
>
|
||||
{iconLeft && (
|
||||
<View style={textInputStyles.icon}>{renderIcon(iconLeft)}</View>
|
||||
<View style={GStyles.inputIcon}>{renderIcon(iconLeft)}</View>
|
||||
)}
|
||||
<RNTextInput
|
||||
style={[textInputStyles.input, { color: fontColor }]}
|
||||
style={[
|
||||
GStyles.inputText,
|
||||
{ color: fontColor },
|
||||
disabled && GStyles.inputPlaceholderDisabled, // <-- placeholder saat disabled
|
||||
]}
|
||||
editable={!disabled}
|
||||
secureTextEntry={secureTextEntry && !isPasswordVisible}
|
||||
keyboardType={keyboardType}
|
||||
onChangeText={handleTextChange}
|
||||
maxLength={maxLength}
|
||||
{...rest}
|
||||
/>
|
||||
{secureTextEntry && (
|
||||
<TouchableOpacity
|
||||
onPress={() => setIsPasswordVisible((prev) => !prev)}
|
||||
style={textInputStyles.icon}
|
||||
style={GStyles.inputIcon}
|
||||
>
|
||||
<Ionicons
|
||||
name={isPasswordVisible ? "eye-off" : "eye"}
|
||||
@@ -111,15 +120,17 @@ export const TextInputCustom = ({
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{iconRight && (
|
||||
<View style={textInputStyles.icon}>{renderIcon(iconRight)}</View>
|
||||
<View style={GStyles.inputIcon}>{renderIcon(iconRight)}</View>
|
||||
)}
|
||||
</View>
|
||||
{/* Prioritaskan error eksternal */}
|
||||
{externalError || internalError ? (
|
||||
<Text style={textInputStyles.errorMessage}>
|
||||
<Text style={GStyles.inputErrorMessage}>
|
||||
{externalError || internalError}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextInputCustom;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { TextInputCustom } from "./TextInputCustom";
|
||||
|
||||
export { TextInputCustom };
|
||||
@@ -1,71 +0,0 @@
|
||||
// components/text-input.styles.ts
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
export const textInputStyles = StyleSheet.create({
|
||||
// Container utama input (View luar)
|
||||
container: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
|
||||
// Label di atas input
|
||||
label: {
|
||||
fontSize: 14,
|
||||
marginBottom: 6,
|
||||
fontWeight: "500",
|
||||
color: MainColor.white,
|
||||
},
|
||||
|
||||
// Tanda bintang merah untuk required
|
||||
required: {
|
||||
color: "red",
|
||||
},
|
||||
|
||||
// Pesan error di bawah input
|
||||
errorMessage: {
|
||||
marginTop: 4,
|
||||
fontSize: 12,
|
||||
color: MainColor.red,
|
||||
},
|
||||
|
||||
// Wrapper input (View pembungkus TextInput)
|
||||
inputContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
borderWidth: 1,
|
||||
borderColor: AccentColor.white,
|
||||
backgroundColor: MainColor.white,
|
||||
paddingHorizontal: 12,
|
||||
height: 50,
|
||||
},
|
||||
|
||||
// Style saat disabled
|
||||
disabled: {
|
||||
backgroundColor: "#f9f9f9",
|
||||
borderColor: "#e0e0e0",
|
||||
},
|
||||
|
||||
// Input utama (TextInput)
|
||||
input: {
|
||||
flex: 1,
|
||||
fontSize: 16,
|
||||
paddingVertical: 0,
|
||||
},
|
||||
|
||||
// Ikon di kiri/kanan
|
||||
icon: {
|
||||
marginHorizontal: 4,
|
||||
justifyContent: "center",
|
||||
},
|
||||
|
||||
// Teks ikon jika berupa string
|
||||
iconText: {
|
||||
fontSize: 16,
|
||||
color: "#000",
|
||||
},
|
||||
|
||||
// Border merah jika ada error
|
||||
errorBorder: {
|
||||
borderColor: "red",
|
||||
},
|
||||
});
|
||||
@@ -20,7 +20,7 @@ interface ITabs {
|
||||
}
|
||||
|
||||
interface IMenuDrawerItem {
|
||||
icon: string;
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
path?: string;
|
||||
color?: string;
|
||||
|
||||
@@ -8,19 +8,27 @@ import {
|
||||
ScrollView,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
interface ViewWrapperProps {
|
||||
children: React.ReactNode;
|
||||
withBackground?: boolean;
|
||||
headerComponent?: React.ReactNode;
|
||||
footerComponent?: React.ReactNode;
|
||||
floatingButton?: React.ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const ViewWrapper = ({
|
||||
children,
|
||||
withBackground = false,
|
||||
headerComponent,
|
||||
footerComponent,
|
||||
floatingButton,
|
||||
style,
|
||||
}: ViewWrapperProps) => {
|
||||
const assetBackground = require("../../assets/images/main-background.png");
|
||||
|
||||
@@ -28,8 +36,13 @@ const ViewWrapper = ({
|
||||
<>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
style={{ flex: 1 }}
|
||||
style={{ flex: 1, backgroundColor: MainColor.darkblue }}
|
||||
>
|
||||
{/* Header Sticky */}
|
||||
{headerComponent && (
|
||||
<View style={GStyles.stickyHeader}>{headerComponent}</View>
|
||||
)}
|
||||
|
||||
<ScrollView
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
@@ -40,14 +53,14 @@ const ViewWrapper = ({
|
||||
<ImageBackground
|
||||
source={assetBackground}
|
||||
resizeMode="cover"
|
||||
style={GStyles.imageBackground}
|
||||
style={[GStyles.imageBackground]}
|
||||
>
|
||||
<View style={GStyles.containerWithBackground}>
|
||||
<View style={[GStyles.containerWithBackground, style]}>
|
||||
{children}
|
||||
</View>
|
||||
</ImageBackground>
|
||||
) : (
|
||||
<View style={GStyles.container}>{children}</View>
|
||||
<View style={[GStyles.container, style]}>{children}</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
@@ -57,7 +70,6 @@ const ViewWrapper = ({
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
}}
|
||||
>
|
||||
@@ -69,43 +81,12 @@ const ViewWrapper = ({
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Floating Component (misal: FAB) */}
|
||||
{floatingButton && (
|
||||
<View style={GStyles.floatingContainer}>{floatingButton}</View>
|
||||
)}
|
||||
</KeyboardAvoidingView>
|
||||
|
||||
{/* <SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ flex: 1, backgroundColor: MainColor.soft_darkblue }}
|
||||
>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
|
||||
{withBackground ? (
|
||||
<ImageBackground
|
||||
source={assetBackground}
|
||||
resizeMode="cover"
|
||||
style={GStyles.imageBackground}
|
||||
>
|
||||
<View style={GStyles.containerWithBackground}>{children}</View>
|
||||
</ImageBackground>
|
||||
) : (
|
||||
<View style={GStyles.container}>{children}</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
|
||||
{footerComponent ? (
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{
|
||||
// flex: 1,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
}}
|
||||
>
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
) : null}
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,52 +1,85 @@
|
||||
// Alert
|
||||
import AlertCustom from "./Alert/AlertCustom";
|
||||
// Button
|
||||
import BackButton from "./Button/BackButton";
|
||||
import LeftButtonCustom from "./Button/BackButton";
|
||||
import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
||||
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";
|
||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||
// Text
|
||||
import TextCustom from "./Text/TextCustom";
|
||||
// TextInput
|
||||
import { TextInputCustom } from "./TextInput/TextInputCustom";
|
||||
import TextInputCustom from "./TextInput/TextInputCustom";
|
||||
// TextArea
|
||||
import TextAreaCustom from "./TextArea/TextAreaCustom";
|
||||
// Grid
|
||||
import Grid from "./Grid/GridCustom";
|
||||
// Box
|
||||
import BaseBox from "./Box/BaseBox";
|
||||
// Avatar
|
||||
import AvatarCustom from "./Image/AvatarCustom"
|
||||
import BoxButtonOnFooter from "./Box/BoxButtonOnFooter";
|
||||
import InformationBox from "./Box/InformationBox";
|
||||
// Stack
|
||||
import StackCustom from "./Stack/StackCustom";
|
||||
// Select
|
||||
import SelectCustom from "./Select/SelectCustom";
|
||||
// Image
|
||||
import AvatarCustom from "./Image/AvatarCustom";
|
||||
import LandscapeFrameUploaded from "./Image/LandscapeFrameUploaded";
|
||||
// Divider
|
||||
import DividerCustom from "./Divider/DividerCustom";
|
||||
// Map
|
||||
import MapCustom from "./Map/MapCustom";
|
||||
// Center
|
||||
import CenterCustom from "./Center/CenterCustom";
|
||||
// Clickable
|
||||
import ClickableCustom from "./Clickable/ClickableCustom";
|
||||
// Scroll
|
||||
import ScrollableCustom from "./Scroll/ScrollCustom";
|
||||
|
||||
export {
|
||||
AlertCustom,
|
||||
// Image
|
||||
AvatarCustom,
|
||||
LandscapeFrameUploaded,
|
||||
// Box
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
InformationBox,
|
||||
LeftButtonCustom as BackButton,
|
||||
// Button
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
// Drawer
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
// Grid
|
||||
Grid,
|
||||
// Map
|
||||
MapCustom,
|
||||
// Select
|
||||
SelectCustom,
|
||||
// ShareComponent
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
// Stack
|
||||
StackCustom,
|
||||
// TextArea
|
||||
TextAreaCustom,
|
||||
// Text
|
||||
TextCustom,
|
||||
// TextInput
|
||||
TextInputCustom,
|
||||
// Grid
|
||||
Grid,
|
||||
// Box
|
||||
BaseBox,
|
||||
// Avatar
|
||||
AvatarCustom,
|
||||
// Stack
|
||||
StackCustom,
|
||||
// Select
|
||||
SelectCustom,
|
||||
// ViewWrapper
|
||||
ViewWrapper,
|
||||
// Divider
|
||||
DividerCustom,
|
||||
// Center
|
||||
CenterCustom,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// Scroll
|
||||
ScrollableCustom,
|
||||
};
|
||||
|
||||
@@ -3,23 +3,27 @@ export const MainColor = {
|
||||
darkblue: "#001D3D",
|
||||
soft_darkblue: "#0e3763",
|
||||
yellow: "#E1B525",
|
||||
white: "#D4D0D0",
|
||||
red: "#FF4B4C",
|
||||
orange: "#FF7043",
|
||||
green: "#4CAF4F",
|
||||
white_gray: "#D4D0D0",
|
||||
text_input: "#EDEBEBFF",
|
||||
placeholder: "#999",
|
||||
disabled: "#606360",
|
||||
placeholder: "#888",
|
||||
white: "#ffffff",
|
||||
// disabled color
|
||||
disabled: "#777",
|
||||
};
|
||||
|
||||
export const AccentColor = {
|
||||
blackgray: "#333533FF",
|
||||
blackgray: "#aaa",
|
||||
darkblue: "#002E59",
|
||||
blue: "#00447D",
|
||||
softblue: "#007CBA",
|
||||
skyblue: "#00BFFF",
|
||||
yellow: "#F8A824",
|
||||
white: "#FEFFFE",
|
||||
// disable color
|
||||
disabledBorder: "#aaa",
|
||||
};
|
||||
|
||||
export const AdminColor = {
|
||||
|
||||
@@ -6,12 +6,32 @@ export {
|
||||
ICON_SIZE_MEDIUM,
|
||||
DRAWER_HEIGHT,
|
||||
RADIUS_BUTTON,
|
||||
ICON_SIZE_BUTTON,
|
||||
PADDING_EXTRA_SMALL,
|
||||
PADDING_SMALL,
|
||||
PADDING_MEDIUM,
|
||||
PADDING_LARGE,
|
||||
};
|
||||
|
||||
// Text Size
|
||||
const TEXT_SIZE_SMALL = 12;
|
||||
const TEXT_SIZE_MEDIUM = 14;
|
||||
const TEXT_SIZE_LARGE = 16;
|
||||
|
||||
// Icon Size
|
||||
const ICON_SIZE_BUTTON = 18
|
||||
const ICON_SIZE_SMALL = 20;
|
||||
const ICON_SIZE_MEDIUM = 24;
|
||||
|
||||
// Drawer Height
|
||||
const DRAWER_HEIGHT = 500; // tinggi drawer5
|
||||
const RADIUS_BUTTON = 50
|
||||
|
||||
// Radius Button
|
||||
const RADIUS_BUTTON = 50
|
||||
|
||||
// Padding
|
||||
const PADDING_EXTRA_SMALL = 10
|
||||
const PADDING_SMALL = 12
|
||||
const PADDING_MEDIUM = 16
|
||||
const PADDING_LARGE = 20
|
||||
|
||||
|
||||
100
lib/dummy-data/master-bidang-bisnis.ts
Normal file
100
lib/dummy-data/master-bidang-bisnis.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
const dummyMasterBidangBisnis = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Teknologi dan Digital",
|
||||
slug: "teknologi_dan_digital",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.403Z",
|
||||
updatedAt: "2025-06-16T09:57:16.403Z",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Kuliner",
|
||||
slug: "kuliner",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.424Z",
|
||||
updatedAt: "2025-06-16T09:57:16.424Z",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "Fashion dan Kecantikan",
|
||||
slug: "fashion_dan_kecantikan",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.442Z",
|
||||
updatedAt: "2025-06-16T09:57:16.442Z",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "Otomotif",
|
||||
slug: "otomotif",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.459Z",
|
||||
updatedAt: "2025-06-16T09:57:16.459Z",
|
||||
},
|
||||
{
|
||||
id: "5",
|
||||
name: "Industri Kreatif",
|
||||
slug: "industri_kreatif",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.471Z",
|
||||
updatedAt: "2025-06-16T09:57:16.471Z",
|
||||
},
|
||||
{
|
||||
id: "6",
|
||||
name: "Konstruksi dan Pertukangan",
|
||||
slug: "konstruksi_dan_pertukangan",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.483Z",
|
||||
updatedAt: "2025-06-16T09:57:16.483Z",
|
||||
},
|
||||
{
|
||||
id: "7",
|
||||
name: "Agribisnis dan Pertanian",
|
||||
slug: "agribisnis_dan_pertanian",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.492Z",
|
||||
updatedAt: "2025-06-16T09:57:16.492Z",
|
||||
},
|
||||
{
|
||||
id: "8",
|
||||
name: "Jasa Umum",
|
||||
slug: "jasa_umum",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.502Z",
|
||||
updatedAt: "2025-06-16T09:57:16.502Z",
|
||||
},
|
||||
{
|
||||
id: "9",
|
||||
name: "Edukasi dan Pelatihan",
|
||||
slug: "edukasi_dan_pelatihan",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.517Z",
|
||||
updatedAt: "2025-06-16T09:57:16.517Z",
|
||||
},
|
||||
{
|
||||
id: "10",
|
||||
name: "Keuangan dan Investasi",
|
||||
slug: "keuangan_dan_investasi",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.527Z",
|
||||
updatedAt: "2025-06-16T09:57:16.527Z",
|
||||
},
|
||||
{
|
||||
id: "11",
|
||||
name: "Perdagangan Umum",
|
||||
slug: "perdagangan_umum",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.537Z",
|
||||
updatedAt: "2025-06-16T09:57:16.537Z",
|
||||
},
|
||||
{
|
||||
id: "12",
|
||||
name: "Pariwisata dan Hospitality",
|
||||
slug: "pariwisata_dan_hospitality",
|
||||
active: true,
|
||||
createdAt: "2025-06-16T09:57:16.547Z",
|
||||
updatedAt: "2025-06-16T09:57:16.547Z",
|
||||
},
|
||||
];
|
||||
|
||||
export default dummyMasterBidangBisnis;
|
||||
734
lib/dummy-data/master-sub-bidang-bisnis.ts
Normal file
734
lib/dummy-data/master-sub-bidang-bisnis.ts
Normal file
@@ -0,0 +1,734 @@
|
||||
const dummyMasterSubBidangBisnis = [
|
||||
{
|
||||
id: "TEK-01",
|
||||
name: "Software Developer",
|
||||
slug: "software_developer",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.406Z",
|
||||
updatedAt: "2025-06-16T09:57:16.406Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-02",
|
||||
name: "Web Developer",
|
||||
slug: "web_developer",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.408Z",
|
||||
updatedAt: "2025-06-16T09:57:16.408Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-03",
|
||||
name: "Mobile App Developer",
|
||||
slug: "mobile_app_developer",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.411Z",
|
||||
updatedAt: "2025-06-16T09:57:16.411Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-04",
|
||||
name: "Konsultan IT",
|
||||
slug: "konsultan_it",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.413Z",
|
||||
updatedAt: "2025-06-16T09:57:16.413Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-05",
|
||||
name: "Digital Marketing",
|
||||
slug: "digital_marketing",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.415Z",
|
||||
updatedAt: "2025-06-16T09:57:16.415Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-06",
|
||||
name: "Cybersecurity",
|
||||
slug: "cybersecurity",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.417Z",
|
||||
updatedAt: "2025-06-16T09:57:16.417Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-07",
|
||||
name: "AI & Machine Learning Services",
|
||||
slug: "ai_and_machine_learning_services",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.419Z",
|
||||
updatedAt: "2025-06-16T09:57:16.419Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-08",
|
||||
name: "Data Analyst/Data Scientist",
|
||||
slug: "data_analyst_data_scientist",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.421Z",
|
||||
updatedAt: "2025-06-16T09:57:16.421Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "TEK-09",
|
||||
name: "Blockchain Developer",
|
||||
slug: "blockchain_developer",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.423Z",
|
||||
updatedAt: "2025-06-16T09:57:16.423Z",
|
||||
masterBidangBisnisId: "1",
|
||||
},
|
||||
{
|
||||
id: "KUL-01",
|
||||
name: "Restoran",
|
||||
slug: "restoran",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.426Z",
|
||||
updatedAt: "2025-06-16T09:57:16.426Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-02",
|
||||
name: "Kafe",
|
||||
slug: "kafe",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.428Z",
|
||||
updatedAt: "2025-06-16T09:57:16.428Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-03",
|
||||
name: "Warung Makan",
|
||||
slug: "warung_makan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.431Z",
|
||||
updatedAt: "2025-06-16T09:57:16.431Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-04",
|
||||
name: "Catering",
|
||||
slug: "catering",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.433Z",
|
||||
updatedAt: "2025-06-16T09:57:16.433Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-05",
|
||||
name: "Food Truck",
|
||||
slug: "food_truck",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.435Z",
|
||||
updatedAt: "2025-06-16T09:57:16.435Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-06",
|
||||
name: "Minuman Kekinian",
|
||||
slug: "minuman_kekinian",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.437Z",
|
||||
updatedAt: "2025-06-16T09:57:16.437Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-07",
|
||||
name: "Toko Roti & Kue",
|
||||
slug: "toko_roti_kue",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.438Z",
|
||||
updatedAt: "2025-06-16T09:57:16.438Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "KUL-08",
|
||||
name: "Supplier Bahan Makanan",
|
||||
slug: "supplier_bahan_makanan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.440Z",
|
||||
updatedAt: "2025-06-16T09:57:16.440Z",
|
||||
masterBidangBisnisId: "2",
|
||||
},
|
||||
{
|
||||
id: "FAS-01",
|
||||
name: "Pakaian Dewasa & Anak",
|
||||
slug: "pakaian_dewasa_anak",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.444Z",
|
||||
updatedAt: "2025-06-16T09:57:16.444Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-02",
|
||||
name: "Butik",
|
||||
slug: "butik",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.445Z",
|
||||
updatedAt: "2025-06-16T09:57:16.445Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-03",
|
||||
name: "Desainer Fashion",
|
||||
slug: "desainer_fashion",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.448Z",
|
||||
updatedAt: "2025-06-16T09:57:16.448Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-04",
|
||||
name: "Aksesoris & Perhiasan",
|
||||
slug: "aksesoris_perhiasan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.449Z",
|
||||
updatedAt: "2025-06-16T09:57:16.449Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-05",
|
||||
name: "Kosmetik",
|
||||
slug: "kosmetik",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.451Z",
|
||||
updatedAt: "2025-06-16T09:57:16.451Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-06",
|
||||
name: "Skincare",
|
||||
slug: "skincare",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.453Z",
|
||||
updatedAt: "2025-06-16T09:57:16.453Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-07",
|
||||
name: "Salon Kecantikan",
|
||||
slug: "salon_kecantikan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.455Z",
|
||||
updatedAt: "2025-06-16T09:57:16.455Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "FAS-08",
|
||||
name: "Barbershop",
|
||||
slug: "barbershop",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.456Z",
|
||||
updatedAt: "2025-06-16T09:57:16.456Z",
|
||||
masterBidangBisnisId: "3",
|
||||
},
|
||||
{
|
||||
id: "OTO-01",
|
||||
name: "Jual Beli Mobil/Motor",
|
||||
slug: "jual_beli_mobil_motor",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.461Z",
|
||||
updatedAt: "2025-06-16T09:57:16.461Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "OTO-02",
|
||||
name: "Bengkel Mobil/Motor",
|
||||
slug: "bengkel_mobil_motor",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.463Z",
|
||||
updatedAt: "2025-06-16T09:57:16.463Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "OTO-03",
|
||||
name: "Aksesori Kendaraan",
|
||||
slug: "aksesori_kendaraan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.465Z",
|
||||
updatedAt: "2025-06-16T09:57:16.465Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "OTO-04",
|
||||
name: "Rental Kendaraan",
|
||||
slug: "rental_kendaraan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.466Z",
|
||||
updatedAt: "2025-06-16T09:57:16.466Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "OTO-05",
|
||||
name: "Cuci Mobil/Motor",
|
||||
slug: "cuci_mobil_motor",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.468Z",
|
||||
updatedAt: "2025-06-16T09:57:16.468Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "OTO-06",
|
||||
name: "Spare Part & Mesin Mobil",
|
||||
slug: "spare_part_mesin_mobil",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.469Z",
|
||||
updatedAt: "2025-06-16T09:57:16.469Z",
|
||||
masterBidangBisnisId: "4",
|
||||
},
|
||||
{
|
||||
id: "INK-01",
|
||||
name: "Fotografi & Videografi",
|
||||
slug: "fotografi_videografi",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.472Z",
|
||||
updatedAt: "2025-06-16T09:57:16.472Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-02",
|
||||
name: "Event Organizer",
|
||||
slug: "event_organizer",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.473Z",
|
||||
updatedAt: "2025-06-16T09:57:16.473Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-03",
|
||||
name: "Desain Grafis",
|
||||
slug: "desain_grafis",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.475Z",
|
||||
updatedAt: "2025-06-16T09:57:16.475Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-04",
|
||||
name: "Advertising & Branding",
|
||||
slug: "advertising_branding",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.476Z",
|
||||
updatedAt: "2025-06-16T09:57:16.476Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-05",
|
||||
name: "Jasa Percetakan",
|
||||
slug: "jasa_percetakan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.477Z",
|
||||
updatedAt: "2025-06-16T09:57:16.477Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-06",
|
||||
name: "Dekorasi & Wedding Planner",
|
||||
slug: "dekorasi_wedding_planner",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.480Z",
|
||||
updatedAt: "2025-06-16T09:57:16.480Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "INK-07",
|
||||
name: "Studio Musik & Produksi",
|
||||
slug: "studio_musik_produksi",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.481Z",
|
||||
updatedAt: "2025-06-16T09:57:16.481Z",
|
||||
masterBidangBisnisId: "5",
|
||||
},
|
||||
{
|
||||
id: "KON-01",
|
||||
name: "Kontraktor Bangunan",
|
||||
slug: "kontraktor_bangunan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.484Z",
|
||||
updatedAt: "2025-06-16T09:57:16.484Z",
|
||||
masterBidangBisnisId: "6",
|
||||
},
|
||||
{
|
||||
id: "KON-02",
|
||||
name: "Arsitek",
|
||||
slug: "arsitek",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.486Z",
|
||||
updatedAt: "2025-06-16T09:57:16.486Z",
|
||||
masterBidangBisnisId: "6",
|
||||
},
|
||||
{
|
||||
id: "KON-03",
|
||||
name: "Desain Interior",
|
||||
slug: "desain_interior",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.487Z",
|
||||
updatedAt: "2025-06-16T09:57:16.487Z",
|
||||
masterBidangBisnisId: "6",
|
||||
},
|
||||
{
|
||||
id: "KON-04",
|
||||
name: "Supplier Material Bangunan",
|
||||
slug: "supplier_material_bangunan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.489Z",
|
||||
updatedAt: "2025-06-16T09:57:16.489Z",
|
||||
masterBidangBisnisId: "6",
|
||||
},
|
||||
{
|
||||
id: "KON-05",
|
||||
name: "Tukang & Renovasi Rumah",
|
||||
slug: "tukang_renovasi_rumah",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.490Z",
|
||||
updatedAt: "2025-06-16T09:57:16.490Z",
|
||||
masterBidangBisnisId: "6",
|
||||
},
|
||||
{
|
||||
id: "AGR-01",
|
||||
name: "Perkebunan",
|
||||
slug: "perkebunan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.493Z",
|
||||
updatedAt: "2025-06-16T09:57:16.493Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "AGR-02",
|
||||
name: "Peternakan",
|
||||
slug: "peternakan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.496Z",
|
||||
updatedAt: "2025-06-16T09:57:16.496Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "AGR-03",
|
||||
name: "Perikanan",
|
||||
slug: "perikanan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.497Z",
|
||||
updatedAt: "2025-06-16T09:57:16.497Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "AGR-04",
|
||||
name: "Supplier Bibit & Pupuk",
|
||||
slug: "supplier_bibit_pupuk",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.498Z",
|
||||
updatedAt: "2025-06-16T09:57:16.498Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "AGR-05",
|
||||
name: "Hasil Tani Organik",
|
||||
slug: "hasil_tani_organik",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.500Z",
|
||||
updatedAt: "2025-06-16T09:57:16.500Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "AGR-06",
|
||||
name: "Alat & Mesin Pertanian",
|
||||
slug: "alat_mesin_pertanian",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.501Z",
|
||||
updatedAt: "2025-06-16T09:57:16.501Z",
|
||||
masterBidangBisnisId: "7",
|
||||
},
|
||||
{
|
||||
id: "JAS-01",
|
||||
name: "Jasa Kebersihan",
|
||||
slug: "jasa_kebersihan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.504Z",
|
||||
updatedAt: "2025-06-16T09:57:16.504Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-02",
|
||||
name: "Laundry",
|
||||
slug: "laundry",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.505Z",
|
||||
updatedAt: "2025-06-16T09:57:16.505Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-03",
|
||||
name: "Penitipan Anak",
|
||||
slug: "penitipan_anak",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.506Z",
|
||||
updatedAt: "2025-06-16T09:57:16.506Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-04",
|
||||
name: "Jasa Keamanan",
|
||||
slug: "jasa_keamanan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.508Z",
|
||||
updatedAt: "2025-06-16T09:57:16.508Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-05",
|
||||
name: "Jasa Pengiriman/Logistik",
|
||||
slug: "jasa_pengiriman_logistik",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.511Z",
|
||||
updatedAt: "2025-06-16T09:57:16.511Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-06",
|
||||
name: "Jasa Ekspedisi",
|
||||
slug: "jasa_ekspedisi",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.513Z",
|
||||
updatedAt: "2025-06-16T09:57:16.513Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-07",
|
||||
name: "Konsultan Bisnis",
|
||||
slug: "konsultan_bisnis",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.514Z",
|
||||
updatedAt: "2025-06-16T09:57:16.514Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "JAS-08",
|
||||
name: "Jasa Hukum/Legal",
|
||||
slug: "jasa_hukum_legal",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.516Z",
|
||||
updatedAt: "2025-06-16T09:57:16.516Z",
|
||||
masterBidangBisnisId: "8",
|
||||
},
|
||||
{
|
||||
id: "EDU-01",
|
||||
name: "Bimbingan Belajar",
|
||||
slug: "bimbingan_belajar",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.518Z",
|
||||
updatedAt: "2025-06-16T09:57:16.518Z",
|
||||
masterBidangBisnisId: "9",
|
||||
},
|
||||
{
|
||||
id: "EDU-02",
|
||||
name: "Kursus Bahasa",
|
||||
slug: "kursus_bahasa",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.520Z",
|
||||
updatedAt: "2025-06-16T09:57:16.520Z",
|
||||
masterBidangBisnisId: "9",
|
||||
},
|
||||
{
|
||||
id: "EDU-03",
|
||||
name: "Pelatihan Digital/Skill",
|
||||
slug: "pelatihan_digital_skill",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.521Z",
|
||||
updatedAt: "2025-06-16T09:57:16.521Z",
|
||||
masterBidangBisnisId: "9",
|
||||
},
|
||||
{
|
||||
id: "EDU-04",
|
||||
name: "LPK (Lembaga Pelatihan Kerja)",
|
||||
slug: "lpk_lembaga_pelatihan_kerja",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.523Z",
|
||||
updatedAt: "2025-06-16T09:57:16.523Z",
|
||||
masterBidangBisnisId: "9",
|
||||
},
|
||||
{
|
||||
id: "EDU-05",
|
||||
name: "Homeschooling",
|
||||
slug: "homeschooling",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.525Z",
|
||||
updatedAt: "2025-06-16T09:57:16.525Z",
|
||||
masterBidangBisnisId: "9",
|
||||
},
|
||||
{
|
||||
id: "KEU-01",
|
||||
name: "Koperasi",
|
||||
slug: "koperasi",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.529Z",
|
||||
updatedAt: "2025-06-16T09:57:16.529Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "KEU-02",
|
||||
name: "FinTEKh",
|
||||
slug: "finTEKh",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.530Z",
|
||||
updatedAt: "2025-06-16T09:57:16.530Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "KEU-03",
|
||||
name: "Konsultan Keuangan",
|
||||
slug: "konsultan_keuangan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.531Z",
|
||||
updatedAt: "2025-06-16T09:57:16.531Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "KEU-04",
|
||||
name: "Investasi & Saham",
|
||||
slug: "investasi_saham",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.532Z",
|
||||
updatedAt: "2025-06-16T09:57:16.532Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "KEU-05",
|
||||
name: "Asuransi",
|
||||
slug: "asuransi",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.534Z",
|
||||
updatedAt: "2025-06-16T09:57:16.534Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "KEU-06",
|
||||
name: "Akuntan Publik",
|
||||
slug: "akuntan_publik",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.535Z",
|
||||
updatedAt: "2025-06-16T09:57:16.535Z",
|
||||
masterBidangBisnisId: "10",
|
||||
},
|
||||
{
|
||||
id: "PER-01",
|
||||
name: "Toko Kelontong",
|
||||
slug: "toko_kelontong",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.538Z",
|
||||
updatedAt: "2025-06-16T09:57:16.538Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PER-02",
|
||||
name: "Minimarket",
|
||||
slug: "minimarket",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.540Z",
|
||||
updatedAt: "2025-06-16T09:57:16.540Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PER-03",
|
||||
name: "Grosir & Distributor",
|
||||
slug: "grosir_distributor",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.541Z",
|
||||
updatedAt: "2025-06-16T09:57:16.541Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PER-04",
|
||||
name: "Dropshipper & Reseller",
|
||||
slug: "dropshipper_reseller",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.543Z",
|
||||
updatedAt: "2025-06-16T09:57:16.543Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PER-05",
|
||||
name: "Marketplace & E-commerce",
|
||||
slug: "marketplace_e_commerce",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.544Z",
|
||||
updatedAt: "2025-06-16T09:57:16.544Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PER-06",
|
||||
name: "Supplier Produk",
|
||||
slug: "supplier_produk",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.546Z",
|
||||
updatedAt: "2025-06-16T09:57:16.546Z",
|
||||
masterBidangBisnisId: "11",
|
||||
},
|
||||
{
|
||||
id: "PAR-01",
|
||||
name: "Agen Travel",
|
||||
slug: "agen_travel",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.549Z",
|
||||
updatedAt: "2025-06-16T09:57:16.549Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-02",
|
||||
name: "Tour Guide",
|
||||
slug: "tour_guide",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.550Z",
|
||||
updatedAt: "2025-06-16T09:57:16.550Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-03",
|
||||
name: "Villa & Penginapan",
|
||||
slug: "villa_penginapan",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.551Z",
|
||||
updatedAt: "2025-06-16T09:57:16.551Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-04",
|
||||
name: "Homestay",
|
||||
slug: "homestay",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.552Z",
|
||||
updatedAt: "2025-06-16T09:57:16.552Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-05",
|
||||
name: "Hotel",
|
||||
slug: "hotel",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.554Z",
|
||||
updatedAt: "2025-06-16T09:57:16.554Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-06",
|
||||
name: "Sewa Motor/Travel",
|
||||
slug: "sewa_motor_travel",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.555Z",
|
||||
updatedAt: "2025-06-16T09:57:16.555Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
{
|
||||
id: "PAR-07",
|
||||
name: "Sovenir & Oleh-Oleh",
|
||||
slug: "sovenir_oleh_oleh",
|
||||
isActive: true,
|
||||
createdAt: "2025-06-16T09:57:16.557Z",
|
||||
updatedAt: "2025-06-16T09:57:16.557Z",
|
||||
masterBidangBisnisId: "12",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
export default dummyMasterSubBidangBisnis;
|
||||
0
lib/index.ts
Normal file
0
lib/index.ts
Normal file
1242
package-lock.json
generated
1242
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
@@ -38,7 +39,9 @@
|
||||
"react-native": "0.79.5",
|
||||
"react-native-gesture-handler": "~2.24.0",
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
"react-native-maps": "1.20.1",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
"react-native-screens": "~4.11.1",
|
||||
@@ -49,9 +52,9 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@types/react": "~19.0.10",
|
||||
"typescript": "~5.8.3",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-config-expo": "~9.2.0"
|
||||
"eslint-config-expo": "~9.2.0",
|
||||
"typescript": "~5.8.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -30,10 +30,12 @@ export default function LoginView() {
|
||||
const id = randomAlfabet + randomNumber + fixNumber;
|
||||
console.log("login user id :", id);
|
||||
|
||||
router.navigate("/verification");
|
||||
// router.navigate(`/(application)/profile/${id}`);
|
||||
// router.navigate("/(application)/home");
|
||||
// router.navigate("/verification");
|
||||
// router.navigate(`/(application)/(user)/profile/${id}`);
|
||||
router.navigate("/(application)/(user)/home");
|
||||
// router.navigate(`/(application)/profile/${id}/edit`);
|
||||
// router.navigate(`/(application)/(user)/portofolio/${id}`)
|
||||
// router.navigate(`/(application)/(image)/preview-image/${id}`);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -55,7 +57,7 @@ export default function LoginView() {
|
||||
fontSize: 10,
|
||||
fontWeight: "thin",
|
||||
fontStyle: "italic",
|
||||
color: MainColor.white,
|
||||
color: MainColor.white_gray,
|
||||
}}
|
||||
>
|
||||
powered by muku.id
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function RegisterView() {
|
||||
const [username, setUsername] = useState("Bagas Banuna");
|
||||
const handleRegister = () => {
|
||||
console.log("Success register", username);
|
||||
router.push("/(application)/home");
|
||||
router.push("/(application)/(user)/home");
|
||||
};
|
||||
return (
|
||||
<>
|
||||
|
||||
74
screens/Forum/CommentarBoxSection.tsx
Normal file
74
screens/Forum/CommentarBoxSection.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
AvatarCustom,
|
||||
TextCustom,
|
||||
ClickableCustom,
|
||||
Spacing,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Entypo } from "@expo/vector-icons";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Forum_CommentarBoxSection({
|
||||
data,
|
||||
setOpenDrawer,
|
||||
setStatus,
|
||||
}: {
|
||||
data: any;
|
||||
setOpenDrawer: (value: boolean) => void;
|
||||
setStatus: (value: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<View>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom href={`/profile/${data.id}`} />
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={8}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TextCustom>{data.name}</TextCustom>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setStatus(data.status);
|
||||
}}
|
||||
style={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Entypo
|
||||
name="dots-three-horizontal"
|
||||
color={MainColor.white}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<TextCustom>{data.deskripsi}</TextCustom>
|
||||
|
||||
<Spacing />
|
||||
<View style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>{data.date}</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
124
screens/Forum/DiscussionBoxSection.tsx
Normal file
124
screens/Forum/DiscussionBoxSection.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
ClickableCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Entypo, Ionicons } from "@expo/vector-icons";
|
||||
import { Href, router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Forum_BoxDetailSection({
|
||||
data,
|
||||
isTruncate,
|
||||
setOpenDrawer,
|
||||
setStatus,
|
||||
href,
|
||||
}: {
|
||||
data: any;
|
||||
isTruncate?: boolean;
|
||||
setOpenDrawer: (value: boolean) => void;
|
||||
setStatus: (value: string) => void;
|
||||
href?: Href;
|
||||
}) {
|
||||
const deskripsiView = (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
padding: 8,
|
||||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
{isTruncate ? (
|
||||
<TextCustom truncate={2}>{data.deskripsi}</TextCustom>
|
||||
) : (
|
||||
<TextCustom>{data.deskripsi}</TextCustom>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<View>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom href={`/profile/${data.id}`} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom>{data.name}</TextCustom>
|
||||
{data.status === "Open" ? (
|
||||
<TextCustom bold size="small" color="green">
|
||||
{data.status}
|
||||
</TextCustom>
|
||||
) : (
|
||||
<TextCustom bold size="small" color="red">
|
||||
{data.status}
|
||||
</TextCustom>
|
||||
)}
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setStatus(data.status);
|
||||
}}
|
||||
style={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Entypo
|
||||
name="dots-three-horizontal"
|
||||
color={MainColor.white}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
{href ? (
|
||||
<ClickableCustom onPress={() => router.push(href as any)}>
|
||||
{deskripsiView}
|
||||
</ClickableCustom>
|
||||
) : (
|
||||
deskripsiView
|
||||
)}
|
||||
|
||||
<Spacing height={10} />
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chatbubble-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
<TextCustom>{data.jumlahBalas}</TextCustom>
|
||||
</View>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom size="small"> {data.date}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
41
screens/Forum/ListPage.tsx
Normal file
41
screens/Forum/ListPage.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export { drawerItemsForumBeranda };
|
||||
|
||||
const drawerItemsForumBeranda = ({
|
||||
id,
|
||||
status,
|
||||
}: {
|
||||
id: string;
|
||||
status: string;
|
||||
}) => [
|
||||
{
|
||||
icon: (
|
||||
<Feather name="edit" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||
),
|
||||
label: "Edit posting",
|
||||
path: `/forum/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon:
|
||||
status === "Open" ? (
|
||||
<Ionicons name="open" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||
) : (
|
||||
<Ionicons name="close" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||
),
|
||||
|
||||
label: status === "Open" ? "Buka forum" : "Tutup forum",
|
||||
path: "",
|
||||
color: status === "Open" ? MainColor.green : MainColor.orange,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons name="trash" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||
),
|
||||
label: "Hapus",
|
||||
path: "",
|
||||
color: MainColor.red,
|
||||
},
|
||||
];
|
||||
41
screens/Forum/MenuDrawerSection.tsx/MenuBeranda.tsx
Normal file
41
screens/Forum/MenuDrawerSection.tsx/MenuBeranda.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import MenuDrawerDynamicGrid from "@/components/Drawer/MenuDrawerDynamicGird";
|
||||
import { router } from "expo-router";
|
||||
import { drawerItemsForumBeranda } from "../ListPage";
|
||||
|
||||
export default function Forum_MenuDrawerBerandaSection({
|
||||
id,
|
||||
status,
|
||||
setIsDrawerOpen,
|
||||
setShowDeleteAlert,
|
||||
setShowAlertStatus,
|
||||
}: {
|
||||
id: string;
|
||||
status: string;
|
||||
setIsDrawerOpen: (value: boolean) => void;
|
||||
setShowDeleteAlert: (value: boolean) => void;
|
||||
setShowAlertStatus: (value: boolean) => void;
|
||||
}) {
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
if (item.label === "Hapus") {
|
||||
setShowDeleteAlert(true);
|
||||
} else if (item.label === "Buka forum" || item.label === "Tutup forum") {
|
||||
setShowAlertStatus(true);
|
||||
} else {
|
||||
router.push(item.path as any);
|
||||
}
|
||||
|
||||
setIsDrawerOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Menu Items */}
|
||||
<MenuDrawerDynamicGrid
|
||||
data={drawerItemsForumBeranda({ id, status })}
|
||||
columns={4} // Ubah ke 2 jika ingin 2 kolom per baris
|
||||
onPressItem={handlePress}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
104
screens/Forum/list-data-dummy.tsx
Normal file
104
screens/Forum/list-data-dummy.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
export {
|
||||
listDummyDiscussionForum,
|
||||
listDummyCommentarForum,
|
||||
}
|
||||
|
||||
const listDummyDiscussionForum = [
|
||||
{
|
||||
name: "Bagas",
|
||||
status: "Open",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 2,
|
||||
},
|
||||
{
|
||||
name: "Banuna",
|
||||
status: "Close",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 8,
|
||||
},
|
||||
{
|
||||
name: "Nusantara",
|
||||
status: "Open",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 5,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Nabillah",
|
||||
status: "Close",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 2,
|
||||
},
|
||||
|
||||
{
|
||||
name: "Riyusa",
|
||||
status: "Close",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 3,
|
||||
},
|
||||
{
|
||||
name: "Nita",
|
||||
status: "Open",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const listDummyCommentarForum = [
|
||||
{
|
||||
name: "Bagas",
|
||||
status: "Delete",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
{
|
||||
name: "Banuna",
|
||||
status: "Report",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
{
|
||||
name: "Nusantara",
|
||||
status: "Delete",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Nabillah",
|
||||
status: "Report",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Riyusa",
|
||||
status: "Report",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
{
|
||||
name: "Nita",
|
||||
status: "Delete",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
},
|
||||
];
|
||||
@@ -1,5 +1,5 @@
|
||||
// import { ITabs } from "@/components/_Interface/types";
|
||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||
import { StackCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { useNavigation } from "expo-router";
|
||||
import React, { useEffect } from "react";
|
||||
@@ -16,21 +16,16 @@ export default function UiHome() {
|
||||
navigation.setOptions({});
|
||||
}, [navigation]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={<TabSection tabs={tabsHome} />}>
|
||||
{/* Content Image */}
|
||||
<Home_ImageSection />
|
||||
<Spacing height={10} />
|
||||
<StackCustom>
|
||||
<Home_ImageSection />
|
||||
|
||||
{/* Grid Section */}
|
||||
<Home_FeatureSection />
|
||||
<Spacing height={10} />
|
||||
<Home_FeatureSection />
|
||||
|
||||
{/* Job Vacancy Section */}
|
||||
<Home_BottomFeatureSection />
|
||||
<Spacing height={20} />
|
||||
<Home_BottomFeatureSection />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Home_FeatureSection() {
|
||||
<View style={stylesHome.gridContainer}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/event/(tabs)")}
|
||||
onPress={() => router.push("/(application)/(user)/event/(tabs)")}
|
||||
>
|
||||
<Ionicons name="analytics" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Event</Text>
|
||||
|
||||
14
screens/Portofolio/BusinessLocationSection.tsx
Normal file
14
screens/Portofolio/BusinessLocationSection.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { BaseBox, MapCustom, StackCustom, TextCustom } from "@/components";
|
||||
|
||||
export default function Portofolio_BusinessLocation() {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Lokasi Bisnis</TextCustom>
|
||||
<MapCustom />
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
14
screens/Portofolio/ButtonDelete.tsx
Normal file
14
screens/Portofolio/ButtonDelete.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ButtonCustom } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function Portofolio_ButtonDelete() {
|
||||
const handleDelete = () => {
|
||||
console.log("Delete");
|
||||
};
|
||||
return (
|
||||
<ButtonCustom textColor={MainColor.white} iconLeft={<Ionicons name="trash-outline" size={20} color="white" />} onPress={handleDelete} backgroundColor={MainColor.red}>
|
||||
Hapus
|
||||
</ButtonCustom>
|
||||
);
|
||||
}
|
||||
173
screens/Portofolio/DataPortofolio.tsx
Normal file
173
screens/Portofolio/DataPortofolio.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
CenterCustom,
|
||||
ClickableCustom,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import DividerCustom from "@/components/Divider/DividerCustom";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Portofolio_Data() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const listData = [
|
||||
{
|
||||
icon: (
|
||||
<FontAwesome name="building-o" size={ICON_SIZE_SMALL} color="white" />
|
||||
),
|
||||
label: "PT.Bali Interakrtif Perkasa",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons name="call-outline" size={ICON_SIZE_SMALL} color="white" />
|
||||
),
|
||||
label: "+6282340374412",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons name="home-outline" size={ICON_SIZE_SMALL} color="white" />
|
||||
),
|
||||
label: "Jl. Raya Kuta No. 123, Bandung, Indonesia",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons name="list-outline" size={ICON_SIZE_SMALL} color="white" />
|
||||
),
|
||||
label: "Teknologia",
|
||||
},
|
||||
];
|
||||
|
||||
const listSubBidang = [
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="chevron-forward-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
),
|
||||
label: "Security System",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="chevron-forward-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
),
|
||||
label: "Web Developers",
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="chevron-forward-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
),
|
||||
label: "Mobile Developers",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom bold>Data Bisnis</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom color="yellow">ID: {id}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
{/* <ClickableCustom
|
||||
style={{ backgroundColor: "blue" }}
|
||||
onPress={() => {
|
||||
router.navigate(`/(application)/(image)/preview-image/${id}`);
|
||||
}}
|
||||
>
|
||||
<AvatarCustom size="xl" />
|
||||
</ClickableCustom> */}
|
||||
|
||||
<ClickableCustom
|
||||
style={{}}
|
||||
onPress={() => {
|
||||
router.navigate(`/(application)/(image)/preview-image/${id}`);
|
||||
}}
|
||||
>
|
||||
<CenterCustom>
|
||||
<AvatarCustom size="xl" />
|
||||
</CenterCustom>
|
||||
</ClickableCustom>
|
||||
|
||||
{/* <Spacing height={10}/> */}
|
||||
|
||||
<View>
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
{item.icon}
|
||||
</Grid.Col>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom style={{ paddingLeft: 5 }}>
|
||||
{item.label}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
<View style={{ paddingLeft: 10 }}>
|
||||
{listSubBidang.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
{item.icon}
|
||||
</Grid.Col>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom style={{ paddingLeft: 5 }}>
|
||||
{item.label}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<DividerCustom labelPosition="top" color={AccentColor.blue} />
|
||||
|
||||
<View>
|
||||
<Grid>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<Ionicons
|
||||
name="pin-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom bold style={{ paddingLeft: 5 }}>
|
||||
Tentang Kami
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<TextCustom style={{ paddingInline: 10 }}>
|
||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit.
|
||||
Doloremque, alias perspiciatis quis enim eos facilis sit est?
|
||||
Doloremque, rerum. Cumque error asperiores harum temporibus
|
||||
cupiditate ullam, id quibusdam! Harum, rerum!
|
||||
</TextCustom>
|
||||
</View>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
62
screens/Portofolio/ListPage.tsx
Normal file
62
screens/Portofolio/ListPage.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Ionicons, FontAwesome5, FontAwesome, Fontisto } from "@expo/vector-icons";
|
||||
|
||||
export const drawerItemsPortofolio = ({ id }: { id: string }): IMenuDrawerItem[] => [
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="create"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit portofolio",
|
||||
path: `/(application)/portofolio/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="camera"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit logo ",
|
||||
path: `/(application)/portofolio/${id}/edit-logo`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<FontAwesome
|
||||
name="id-card-o"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit social media ",
|
||||
path: `/(application)/portofolio/${id}/edit-social-media`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Fontisto
|
||||
name="map-marker-alt"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit Map",
|
||||
path: `/(application)/maps/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<FontAwesome5
|
||||
name="map-pin"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Custom Pin Map",
|
||||
path: `/(application)/maps/${id}/custom-pin`,
|
||||
},
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user