Compare commits
24 Commits
resourcing
...
voting/25-
| Author | SHA1 | Date | |
|---|---|---|---|
| b18044f53f | |||
| 20258d1fe5 | |||
| 51d696128e | |||
| 1b1732c7d8 | |||
| 7528c449eb | |||
| ed87d4a3f3 | |||
| 603003865b | |||
| e02ae8e35d | |||
| 4f8ae2d7e0 | |||
| 360ac5807c | |||
| 8cb0054580 | |||
| 64d5a4308c | |||
| 30d61c84aa | |||
| 70e324e76e | |||
| 4474b46ff3 | |||
| aa4ea9fb0c | |||
| 81d86885f4 | |||
| 814f261881 | |||
| 7889d25a44 | |||
| ce0e82e627 | |||
| 08dfd62bfd | |||
| c8cc0f0232 | |||
| b844a8151d | |||
| f9e96aa077 |
@@ -92,6 +92,122 @@ export default function UserLayout() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Event",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/[id]/list-of-participants"
|
||||
options={{
|
||||
title: "Daftar peserta",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
{/* ========== End Event Section ========= */}
|
||||
|
||||
{/* ========== Collaboration Section ========= */}
|
||||
<Stack.Screen
|
||||
name="collaboration/(tabs)"
|
||||
options={{
|
||||
title: "Collaboration",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/create"
|
||||
options={{
|
||||
title: "Tambah Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/list-of-participants"
|
||||
options={{
|
||||
title: "Daftar Partisipan",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/detail-participant"
|
||||
options={{
|
||||
title: "Partisipasi Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
{/* ========== End Collaboration Section ========= */}
|
||||
|
||||
{/* ========== Voting Section ========= */}
|
||||
<Stack.Screen
|
||||
name="voting/create"
|
||||
options={{
|
||||
title: "Tambah Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/(tabs)"
|
||||
options={{
|
||||
title: "Voting",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* <Stack.Screen
|
||||
name="voting/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* ========== End Voting Section ========= */}
|
||||
|
||||
{/* ========== Job Section ========= */}
|
||||
<Stack.Screen
|
||||
name="job/create"
|
||||
options={{
|
||||
title: "Tambah Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/(tabs)"
|
||||
options={{
|
||||
title: "Job Vacancy",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/index"
|
||||
options={{
|
||||
title: "Detail Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Job Section ========= */}
|
||||
|
||||
{/* ========== Forum Section ========= */}
|
||||
<Stack.Screen
|
||||
name="forum/create"
|
||||
@@ -111,7 +227,7 @@ export default function UserLayout() {
|
||||
name="forum/[id]/forumku"
|
||||
options={{
|
||||
title: "Forumku",
|
||||
headerLeft: () => <BackButton icon={'close'} />,
|
||||
headerLeft: () => <BackButton icon={"close"} />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
|
||||
36
app/(application)/(user)/collaboration/(tabs)/_layout.tsx
Normal file
36
app/(application)/(user)/collaboration/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { IconHome } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function CollaborationTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="participant"
|
||||
options={{
|
||||
title: "Partisipan",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="people" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="group"
|
||||
options={{
|
||||
title: "Grup",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="chatbox-ellipses" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
68
app/(application)/(user)/collaboration/(tabs)/group.tsx
Normal file
68
app/(application)/(user)/collaboration/(tabs)/group.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { BaseBox, Grid, TextCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
|
||||
export default function CollaborationGroup() {
|
||||
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingBlock={5}
|
||||
href={`/collaboration/${index}/${generateProjectName()}/room-chat`}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom bold>{generateProjectName()}</TextCustom>
|
||||
<TextCustom size="small">2 Anggota</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
<Feather name="chevron-right" size={20} color={MainColor.white} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function generateProjectName() {
|
||||
const adjectives = [
|
||||
"Blue",
|
||||
"Dark",
|
||||
"Bright",
|
||||
"Quantum",
|
||||
"Silent",
|
||||
"Cyber",
|
||||
"Epic",
|
||||
"Golden",
|
||||
"Shadow",
|
||||
"Rapid",
|
||||
];
|
||||
|
||||
const nouns = [
|
||||
"Spark",
|
||||
"Core",
|
||||
"Orbit",
|
||||
"Nest",
|
||||
"Drive",
|
||||
"Nova",
|
||||
"Cloud",
|
||||
"Blade",
|
||||
"Matrix",
|
||||
"Link",
|
||||
];
|
||||
|
||||
const randomAdjective =
|
||||
adjectives[Math.floor(Math.random() * adjectives.length)];
|
||||
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
||||
|
||||
return randomAdjective + randomNoun;
|
||||
}
|
||||
28
app/(application)/(user)/collaboration/(tabs)/index.tsx
Normal file
28
app/(application)/(user)/collaboration/(tabs)/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { FloatingButton, ViewWrapper } from "@/components";
|
||||
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function CollaborationBeranda() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() => {
|
||||
router.push("/collaboration/create");
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
href={`/collaboration/${index}`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ButtonCustom, Spacing } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function CollaborationParticipans() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"participant"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const headerComponent = (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
padding: 5,
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 50,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === "participant" ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === "participant" ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress("participant")}
|
||||
>
|
||||
Partisipasi Proyek
|
||||
</ButtonCustom>
|
||||
<Spacing width={"2%"} />
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === "main" ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress("main")}
|
||||
>
|
||||
Proyek Saya
|
||||
</ButtonCustom>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter headerComponent={headerComponent}>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index.toString()}
|
||||
id={index.toString()}
|
||||
username={` ${
|
||||
activeCategory === "participant"
|
||||
? "Partisipasi Proyek"
|
||||
: "Proyek Saya"
|
||||
}`}
|
||||
href={
|
||||
activeCategory === "participant"
|
||||
? `/collaboration/${index}/detail-participant`
|
||||
: `/collaboration/${index}/detail-project-main`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function CollaborationRoomInfo() {
|
||||
const { id, detail } = useLocalSearchParams();
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Info`,
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<BoxWithHeaderSection>
|
||||
<StackCustom>
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.title}</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom>{item.value}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
|
||||
<BoxWithHeaderSection>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<AvatarUsernameAndOtherComponent key={index} avatarHref={`/profile/${index}`} />
|
||||
))}
|
||||
</BoxWithHeaderSection>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Judul Proyek",
|
||||
value: "Judul Proyek",
|
||||
},
|
||||
{
|
||||
title: "Industri",
|
||||
value: "Pilihan Industri",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value: "Deskripsi Proyek",
|
||||
},
|
||||
{
|
||||
title: "Tujuan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
},
|
||||
{
|
||||
title: "Keuntungan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,192 @@
|
||||
import {
|
||||
BackButton,
|
||||
BoxButtonOnFooter,
|
||||
Grid,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
|
||||
export default function CollaborationRoomChat() {
|
||||
const { id, detail } = useLocalSearchParams();
|
||||
|
||||
const inputChat = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
{/* <View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<TextInputCustom placeholder="Ketik pesan..." />
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.5}
|
||||
onPress={() => console.log("Send")}
|
||||
style={{
|
||||
backgroundColor: AccentColor.blue,
|
||||
padding: 10,
|
||||
borderRadius: 50,
|
||||
}}
|
||||
>
|
||||
<Feather name="send" size={30} color={MainColor.white} />
|
||||
</TouchableOpacity>
|
||||
</View> */}
|
||||
<Grid>
|
||||
<Grid.Col span={9}>
|
||||
<TextInputCustom placeholder="Ketik pesan..." />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.5}
|
||||
onPress={() => console.log("Send")}
|
||||
style={{
|
||||
backgroundColor: AccentColor.blue,
|
||||
padding: 10,
|
||||
borderRadius: 50,
|
||||
}}
|
||||
>
|
||||
<Feather
|
||||
name="send"
|
||||
size={30}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Proyek ${detail}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<Feather
|
||||
name="info"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper footerComponent={inputChat()}>
|
||||
{dummyData.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[
|
||||
styles.messageRow,
|
||||
item.role === 1 ? styles.rightAlign : styles.leftAlign,
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.bubble,
|
||||
item.role === 1 ? styles.bubbleRight : styles.bubbleLeft,
|
||||
]}
|
||||
>
|
||||
<TextCustom style={styles.sender}>{item.nama}</TextCustom>
|
||||
<TextCustom style={styles.message}>{item.chat}</TextCustom>
|
||||
<TextCustom style={styles.time}>
|
||||
{new Date(item.time).toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
{/* <TextInputCustom placeholder="Ketik pesan..." />
|
||||
<Spacing/> */}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Hai! Kamu udah lihat dokumen proyek yang baru?",
|
||||
time: "2025-07-24T09:01:15Z",
|
||||
},
|
||||
{
|
||||
nama: "Rafi",
|
||||
role: 2,
|
||||
chat: "Halo! Iya, aku baru aja baca. Kayaknya kita harus revisi bagian akhir deh.",
|
||||
time: "2025-07-24T09:02:03Z",
|
||||
},
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Setuju. Aku juga kurang sreg sama penutupnya.",
|
||||
time: "2025-07-24T09:02:45Z",
|
||||
},
|
||||
{
|
||||
nama: "Rafi",
|
||||
role: 2,
|
||||
chat: "Oke, aku coba edit malam ini ya. Nanti aku share ulang versinya.",
|
||||
time: "2025-07-24T09:03:10Z",
|
||||
},
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Siap, makasih ya. Jangan begadang!",
|
||||
time: "2025-07-24T09:03:30Z",
|
||||
},
|
||||
];
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
messageRow: {
|
||||
flexDirection: "row",
|
||||
marginBottom: 12,
|
||||
},
|
||||
rightAlign: {
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
leftAlign: {
|
||||
justifyContent: "flex-start",
|
||||
},
|
||||
bubble: {
|
||||
maxWidth: "75%",
|
||||
padding: 10,
|
||||
borderRadius: 12,
|
||||
},
|
||||
bubbleRight: {
|
||||
backgroundColor: "#DCF8C6", // hijau muda
|
||||
borderTopRightRadius: 0,
|
||||
},
|
||||
bubbleLeft: {
|
||||
backgroundColor: "#F0F0F0", // abu-abu terang
|
||||
borderTopLeftRadius: 0,
|
||||
},
|
||||
sender: {
|
||||
fontSize: 12,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 2,
|
||||
color: "#555",
|
||||
},
|
||||
message: {
|
||||
fontSize: 15,
|
||||
color: "#000",
|
||||
},
|
||||
time: {
|
||||
fontSize: 10,
|
||||
color: "#888",
|
||||
textAlign: "right",
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
DrawerCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function CollaborationDetailParticipant() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<TextCustom align="center" bold size="large">
|
||||
Partisipan
|
||||
</TextCustom>
|
||||
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<AvatarUsernameAndOtherComponent
|
||||
key={index}
|
||||
avatarHref={`/profile/${index}`}
|
||||
rightComponent={
|
||||
<MaterialIcons
|
||||
name="notes"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
onPress={() => setOpenDrawerParticipant(true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi Diri</TextCustom>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Commodi,
|
||||
itaque adipisci. Voluptas, sed quod! Ad facere labore voluptates,
|
||||
neque quidem aut reprehenderit ducimus mollitia quisquam temporibus!
|
||||
Temporibus iusto soluta necessitatibus.
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import Collaboration_MainParticipanSelectedSection from "@/screens/Collaboration/ProjectMainSelectedSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function CollaborationDetailProjectMain() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
|
||||
const [selected, setSelected] = useState<(string | number)[]>([]);
|
||||
|
||||
const handleEdit = () => {
|
||||
console.log("Edit collaboration");
|
||||
router.push("/(application)/(user)/collaboration/(id)/edit");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Proyek Saya",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<Collaboration_MainParticipanSelectedSection
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
setOpenDrawerParticipant={setOpenDrawerParticipant}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Buat Grup",
|
||||
message:
|
||||
"Apakah anda yakin ingin membuat grup untuk proyek ini ?",
|
||||
textLeft: "Tidak",
|
||||
textRight: "Ya",
|
||||
onPressLeft: () => {},
|
||||
onPressRight: () => {
|
||||
router.navigate(
|
||||
"/(application)/(user)/collaboration/(tabs)/group"
|
||||
);
|
||||
console.log("selected :", selected);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buat Grup
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
label: "Edit",
|
||||
path: "/(application)/(user)/collaboration/(tabs)/group",
|
||||
icon: <IconEdit />,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
handleEdit();
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi Diri</TextCustom>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Commodi,
|
||||
itaque adipisci. Voluptas, sed quod! Ad facere labore voluptates,
|
||||
neque quidem aut reprehenderit ducimus mollitia quisquam temporibus!
|
||||
Temporibus iusto soluta necessitatibus.
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
53
app/(application)/(user)/collaboration/[id]/edit.tsx
Normal file
53
app/(application)/(user)/collaboration/[id]/edit.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function CollaborationEdit() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
|
||||
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={[
|
||||
{ label: "Industri 1", value: "industri-1" },
|
||||
{ label: "Industri 2", value: "industri-2" },
|
||||
{ label: "Industri 3", value: "industri-3" },
|
||||
]}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
title="Update"
|
||||
onPress={() => {
|
||||
console.log("Update proyek");
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
91
app/(application)/(user)/collaboration/[id]/index.tsx
Normal file
91
app/(application)/(user)/collaboration/[id]/index.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function CollaborationDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPartisipasi, setOpenDrawerPartisipasi] = useState(false);
|
||||
const [openDrawerMenu, setOpenDrawerMenu] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Detail Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerMenu(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
|
||||
<ButtonCustom onPress={() => setOpenDrawerPartisipasi(true)}>
|
||||
Partisipasi
|
||||
</ButtonCustom>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Partisipasi */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPartisipasi}
|
||||
closeDrawer={() => setOpenDrawerPartisipasi(false)}
|
||||
height={300}
|
||||
>
|
||||
<TextAreaCustom
|
||||
label="Dekripsi diri"
|
||||
placeholder="Masukan dekripsi diri"
|
||||
required
|
||||
showCount
|
||||
maxLength={500}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
style={{ alignSelf: "flex-end" }}
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Simpan data deskripsi",
|
||||
message: "Apakah anda sudah yakin ingin menyimpan data ini ?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Simpan",
|
||||
onPressRight: () => router.replace(`/collaboration/(tabs)/group`),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Drawer Menu */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerMenu}
|
||||
closeDrawer={() => setOpenDrawerMenu(false)}
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <Ionicons name="people" size={24} color="white" />,
|
||||
label: "Daftar Partisipan",
|
||||
path: `/collaboration/${id}/list-of-participants`,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
router.push(item.path as any);
|
||||
setOpenDrawerMenu(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
DrawerCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { ScrollView } from "react-native";
|
||||
|
||||
export default function CollaborationListOfParticipants() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingBlock={5}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${id}`}
|
||||
rightComponent={
|
||||
<Feather
|
||||
name="chevron-right"
|
||||
size={24}
|
||||
color="white"
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi diri</TextCustom>
|
||||
<BaseBox>
|
||||
<ScrollView style={{ height: "80%" }}>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut iqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.
|
||||
</TextCustom>
|
||||
</ScrollView>
|
||||
</BaseBox>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
53
app/(application)/(user)/collaboration/create.tsx
Normal file
53
app/(application)/(user)/collaboration/create.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function CollaborationCreate() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
|
||||
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={[
|
||||
{ label: "Industri 1", value: "industri-1" },
|
||||
{ label: "Industri 2", value: "industri-2" },
|
||||
{ label: "Industri 3", value: "industri-3" },
|
||||
]}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
title="Simpan"
|
||||
onPress={() => {
|
||||
console.log("Simpan proyek");
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,81 +1,43 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { OS_IOS_HEIGHT, OS_ANDROID_HEIGHT } from "@/constants/constans-value";
|
||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
IconContribution,
|
||||
IconHistory,
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Tabs } from "expo-router";
|
||||
import { Platform, View } from "react-native";
|
||||
|
||||
export default function EventLayout() {
|
||||
export default function EventTabsLayout() {
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
tabBarActiveTintColor: MainColor.yellow,
|
||||
tabBarInactiveTintColor: MainColor.white_gray,
|
||||
tabBarBackground: CustomTabBarBackground,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: OS_IOS_HEIGHT,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: OS_ANDROID_HEIGHT,
|
||||
},
|
||||
default: {},
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Home",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="home" color={color} />
|
||||
),
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="list" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="kontribusi"
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="extension-puzzle" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="riwayat"
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome5 size={20} name="history" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
function CustomTabBarBackground() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: AccentColor.blue,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
43
app/(application)/(user)/event/(tabs)/contribution.tsx
Normal file
43
app/(application)/(user)/event/(tabs)/contribution.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import React from "react";
|
||||
|
||||
export default function EventContribution() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BoxWithHeaderSection key={index} href={`/event/${index}/contribution`}>
|
||||
<StackCustom>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${index}`}
|
||||
rightComponent={
|
||||
<TextCustom truncate>
|
||||
{new Date().toLocaleDateString()}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextCustom bold align="center" size="xlarge">
|
||||
Judul Event Disini
|
||||
</TextCustom>
|
||||
|
||||
<Grid>
|
||||
{Array.from({ length: 4 }).map((_, index2) => (
|
||||
<Grid.Col span={3} key={index2}>
|
||||
<AvatarCustom size="sm" href={`/profile/${index2}`} />
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
68
app/(application)/(user)/event/(tabs)/history.tsx
Normal file
68
app/(application)/(user)/event/(tabs)/history.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { ButtonCustom, Spacing, TextCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function EventHistory() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const headerComponent = (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
padding: 5,
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 50,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === "all" ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={activeCategory === "all" ? MainColor.black : MainColor.white}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress("all")}
|
||||
>
|
||||
Semua Riwayat
|
||||
</ButtonCustom>
|
||||
<Spacing width={"2%"} />
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === "main" ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress("main")}
|
||||
>
|
||||
Riwayat Saya
|
||||
</ButtonCustom>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={headerComponent} hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index.toString()}
|
||||
id={index.toString()}
|
||||
username={`Riwayat ${activeCategory === "main" ? "Saya" : "Semua"}`}
|
||||
rightComponentAvatar={
|
||||
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
|
||||
}
|
||||
href={`/event/${index}/history`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +1,23 @@
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
import { Text, TouchableHighlight, View } from "react-native";
|
||||
|
||||
export default function Event() {
|
||||
export default function EventBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/event/create")} />
|
||||
}
|
||||
>
|
||||
<TouchableHighlight onPress={() => router.push("/event/detail/1")}>
|
||||
<View
|
||||
style={{
|
||||
padding: 20,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
borderRadius: 10,
|
||||
borderColor: AccentColor.blue,
|
||||
borderWidth: 1,
|
||||
}}
|
||||
>
|
||||
<Text style={GStyles.textLabel}>Event</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
href={`/event/${index}/publish`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Text } from "react-native";
|
||||
|
||||
export default function Kontribusi() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Text style={GStyles.textLabel}>Kontribusi</Text>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Text } from "react-native";
|
||||
|
||||
export default function Riwayat() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Text style={GStyles.textLabel}>Riwayat</Text>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,63 @@
|
||||
import {
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Text } from "react-native";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function EventStatus() {
|
||||
const id = "test-id-event";
|
||||
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
|
||||
export default function Status() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Text style={GStyles.textLabel}>Status</Text>
|
||||
<ViewWrapper headerComponent={scrollComponent}>
|
||||
<BoxWithHeaderSection href={`/event/${id}/${activeCategory}/detail-event`}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<Grid>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom truncate bold>
|
||||
Lorem ipsum,{" "}
|
||||
<TextCustom color="green">{activeCategory}</TextCustom> dolor
|
||||
sit amet consectetur adipisicing elit.
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<TextCustom truncate={2}>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur
|
||||
eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora,
|
||||
atque. Aperiam minima asperiores dicta perferendis quis adipisci,
|
||||
dolore optio porro!
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
117
app/(application)/(user)/event/[id]/[status]/detail-event.tsx
Normal file
117
app/(application)/(user)/event/[id]/[status]/detail-event.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import {
|
||||
BaseBox,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
Grid,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_AlertButtonStatusSection from "@/screens/Event/AlertButtonStatusSection";
|
||||
import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection";
|
||||
import { menuDrawerDraftEvent } from "@/screens/Event/menuDrawerDraft";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function EventDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
const [openDeleteAlert, setOpenDeleteAlert] = useState(false);
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail ${status === "publish" ? "" : status}`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom bold align="center" size="xlarge">
|
||||
Judul event {status}
|
||||
</TextCustom>
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.title}</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom>{item.value}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
<Event_ButtonStatusSection
|
||||
status={status as string}
|
||||
onOpenAlert={setOpenAlert}
|
||||
onOpenDeleteAlert={setOpenDeleteAlert}
|
||||
/>
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerDraftEvent({ id: id as string }) as any}
|
||||
columns={4}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
<Event_AlertButtonStatusSection
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
openAlert={openAlert}
|
||||
setOpenAlert={setOpenAlert}
|
||||
openDeleteAlert={openDeleteAlert}
|
||||
setOpenDeleteAlert={setOpenDeleteAlert}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Lokasi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
{
|
||||
title: "Tipe Acara",
|
||||
value: "Workshop",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Mulai",
|
||||
value: "Senin, 18 Juli 2025, 10:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Berakhir",
|
||||
value: "Selasa, 19 Juli 2025, 12:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
];
|
||||
51
app/(application)/(user)/event/[id]/contribution.tsx
Normal file
51
app/(application)/(user)/event/[id]/contribution.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
Spacing,
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function EventDetailContribution() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail kontribusi`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
107
app/(application)/(user)/event/[id]/edit.tsx
Normal file
107
app/(application)/(user)/event/[id]/edit.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
|
||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||
import { router } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export default function EventEdit() {
|
||||
const [selectedDate, setSelectedDate] = useState<
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
|
||||
const [selectedEndDate, setSelectedEndDate] = useState<
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
|
||||
const handlerSubmit = () => {
|
||||
try {
|
||||
if (selectedDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal belum dipilih");
|
||||
}
|
||||
|
||||
if (selectedEndDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal berakhir belum dipilih");
|
||||
}
|
||||
|
||||
console.log("Data berhasil terupdate");
|
||||
router.back()
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = (
|
||||
<ButtonCustom title="Update" onPress={handlerSubmit} />
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
placeholder="Masukkan nama event"
|
||||
label="Nama Event"
|
||||
required
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Tipe Event"
|
||||
placeholder="Pilih tipe event"
|
||||
data={masterTypeEvent}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukkan lokasi event"
|
||||
required
|
||||
/>
|
||||
|
||||
<DateTimePickerCustom
|
||||
label="Tanggal & Waktu Mulai"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedDate(date as any);
|
||||
}}
|
||||
value={selectedDate as any}
|
||||
minimumDate={new Date(Date.now())}
|
||||
/>
|
||||
|
||||
<DateTimePickerCustom
|
||||
label="Tanggal & Waktu Berakhir"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedEndDate(date as any);
|
||||
}}
|
||||
value={selectedEndDate as any}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukkan deskripsi event"
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
/>
|
||||
|
||||
{buttonSubmit}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
51
app/(application)/(user)/event/[id]/history.tsx
Normal file
51
app/(application)/(user)/event/[id]/history.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
Spacing,
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function EventDetailHistory() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail riwayat`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
17
app/(application)/(user)/event/[id]/list-of-participants.tsx
Normal file
17
app/(application)/(user)/event/[id]/list-of-participants.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
|
||||
export default function EventListOfParticipants() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingBlock={0}>
|
||||
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${index}`} />
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
64
app/(application)/(user)/event/[id]/publish.tsx
Normal file
64
app/(application)/(user)/event/[id]/publish.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Alert } from "react-native";
|
||||
|
||||
export default function EventDetailPublish() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
const footerButton = (
|
||||
<ButtonCustom
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
onPress={() => Alert.alert("Anda berhasil join event ini")}
|
||||
>
|
||||
Join
|
||||
</ButtonCustom>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Event publish`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection footerButton={footerButton} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
|
||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||
import { router } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
@@ -23,32 +23,39 @@ export default function EventCreate() {
|
||||
>(null);
|
||||
|
||||
const handlerSubmit = () => {
|
||||
if (selectedDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.warn("Tanggal belum dipilih");
|
||||
}
|
||||
try {
|
||||
if (selectedDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal belum dipilih");
|
||||
}
|
||||
|
||||
if (selectedEndDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.warn("Tanggal belum dipilih");
|
||||
if (selectedEndDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal berakhir belum dipilih");
|
||||
}
|
||||
|
||||
console.log("Data berhasil disimpan");
|
||||
router.navigate("/event/status");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom title="Simpan" onPress={handlerSubmit} />
|
||||
</BoxButtonOnFooter>
|
||||
<ButtonCustom title="Simpan" onPress={handlerSubmit} />
|
||||
// <BoxButtonOnFooter>
|
||||
// </BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonSubmit}>
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
placeholder="Masukkan nama event"
|
||||
@@ -93,6 +100,8 @@ export default function EventCreate() {
|
||||
showCount
|
||||
maxLength={100}
|
||||
/>
|
||||
|
||||
{buttonSubmit}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
|
||||
@@ -3,16 +3,14 @@ import {
|
||||
AvatarCustom,
|
||||
BackButton,
|
||||
DrawerCustom,
|
||||
TextInputCustom,
|
||||
SearchInput,
|
||||
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";
|
||||
|
||||
@@ -34,20 +32,7 @@ export default function Forum() {
|
||||
/>
|
||||
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari topik forum..."
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari topik diskusi" />}
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
|
||||
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="archive"
|
||||
options={{
|
||||
title: "Arsip",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="archive" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
|
||||
export default function JobArchive() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox key={i} paddingTop={20} paddingBottom={20}>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/job/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari pekerjaan" />}
|
||||
>
|
||||
{jobDataDummy.map((item, index) => (
|
||||
<BoxWithHeaderSection key={index} onPress={() => router.push(`/job/${item.id}`)}>
|
||||
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${item.id}`} />
|
||||
<Spacing />
|
||||
<TextCustom truncate={2} align="center" bold size="large">
|
||||
{item.posisi}
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobStatus() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/job/${e.id}/${activeCategory}/detail`}
|
||||
// onPress={() => console.log("pressed")}
|
||||
>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi} {activeCategory?.toUpperCase()}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail} />
|
||||
<Job_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit",
|
||||
path: `/job/${id}/edit`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobEdit() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { Alert, Linking } from "react-native";
|
||||
|
||||
export default function JobDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const OpenLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const openInBrowser = async () => {
|
||||
const supported = await Linking.canOpenURL(jobUrl);
|
||||
if (supported) {
|
||||
await Linking.openURL(jobUrl);
|
||||
} else {
|
||||
Alert.alert("Gagal membuka link", "Browser tidak tersedia.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={
|
||||
<Ionicons name="globe" size={ICON_SIZE_SMALL} color="white" />
|
||||
}
|
||||
onPress={openInBrowser}
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
>
|
||||
Buka Lowongan di Browser
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
const CopyLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await Clipboard.setStringAsync(jobUrl);
|
||||
Alert.alert(
|
||||
"Link disalin",
|
||||
"Tautan lowongan telah disalin ke clipboard."
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={<Ionicons name="copy" size={ICON_SIZE_SMALL} color="white" />}
|
||||
onPress={copyToClipboard}
|
||||
backgroundColor={MainColor.orange}
|
||||
textColor="white"
|
||||
>
|
||||
Salin Link
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail}/>
|
||||
<OpenLinkButton />
|
||||
<Spacing />
|
||||
<CopyLinkButton />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
73
app/(application)/(user)/job/create.tsx
Normal file
73
app/(application)/(user)/job/create.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobCreate() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/job/(tabs)/status")
|
||||
}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -11,18 +11,18 @@ 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" },
|
||||
{ value: "all", label: "Semua" },
|
||||
{ value: "event", label: "Event" },
|
||||
{ value: "job", label: "Job" },
|
||||
{ value: "voting", label: "Voting" },
|
||||
{ value: "donasi", label: "Donasi" },
|
||||
{ value: "investasi", label: "Investasi" },
|
||||
{ value: "forum", label: "Forum" },
|
||||
{ value: "collaboration", label: "Collaboration" },
|
||||
];
|
||||
|
||||
const selectedCategory = (id: number) => {
|
||||
const category = categories.find((c) => c.id === id);
|
||||
const selectedCategory = (value: string) => {
|
||||
const category = categories.find((c) => c.value === value);
|
||||
return category?.label;
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ const BoxNotification = ({
|
||||
activeCategory,
|
||||
}: {
|
||||
index: number;
|
||||
activeCategory: number | null;
|
||||
activeCategory: string | null;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -39,13 +39,13 @@ const BoxNotification = ({
|
||||
onPress={() =>
|
||||
console.log(
|
||||
"Notification >",
|
||||
selectedCategory(activeCategory as number)
|
||||
selectedCategory(activeCategory as string)
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>
|
||||
# {selectedCategory(activeCategory as number)}
|
||||
# {selectedCategory(activeCategory as string)}
|
||||
</TextCustom>
|
||||
|
||||
<View
|
||||
@@ -81,38 +81,31 @@ const BoxNotification = ({
|
||||
};
|
||||
|
||||
export default function Notifications() {
|
||||
const [activeCategory, setActiveCategory] = useState<number | null>(1);
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.id);
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
return (
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<ScrollableCustom
|
||||
data={categories}
|
||||
data={categories.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
activeId={activeCategory as string}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 20 }).map((e, i) => (
|
||||
<View key={i}>
|
||||
<BoxNotification index={i} activeCategory={activeCategory} />
|
||||
<BoxNotification index={i} activeCategory={activeCategory as any} />
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
||||
import Profile_MenuDrawerSection from "@/screens/Profile/MenuDrawerSection";
|
||||
import ProfilSection from "@/screens/Profile/ProfilSection";
|
||||
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
|
||||
import ProfileSection from "@/screens/Profile/ProfileSection";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -52,7 +52,7 @@ export default function Profile() {
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
<ProfilSection />
|
||||
<ProfileSection />
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
|
||||
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
IconContribution,
|
||||
IconHistory,
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function VotingTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
27
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
27
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
|
||||
export default function VotingContribution() {
|
||||
const bottomComponent = (
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Pilihan Anda:</TextCustom>
|
||||
<BadgeCustom style={[GStyles.alignSelfCenter]}>Pilihan 1</BadgeCustom>
|
||||
</StackCustom>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection
|
||||
key={index}
|
||||
bottomComponent={bottomComponent}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
9
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
9
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { TextCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function VotingHistory() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<TextCustom>Voting History</TextCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function VotingBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/voting/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari voting" />}
|
||||
>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection key={index} />
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
61
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
61
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingStatus() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
// href={`/job/${e.id}/${activeCategory}/detail`}
|
||||
// onPress={() => console.log("pressed")}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
Lorem ipsum dolor sit {activeCategory}
|
||||
</TextCustom>
|
||||
<BadgeCustom
|
||||
style={{ width: "70%", alignSelf: "center" }}
|
||||
variant="light"
|
||||
>
|
||||
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||
</BadgeCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
78
app/(application)/(user)/voting/create.tsx
Normal file
78
app/(application)/(user)/voting/create.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function CreateVoting() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/voting/(tabs)/status")
|
||||
}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul Voting"
|
||||
placeholder="MasukanJudul Voting"
|
||||
required
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukan Deskripsi"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<DateTimePickerCustom label="Mulai Voting" required />
|
||||
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextInputCustom
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
/>
|
||||
</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 />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
258
app/(application)/coba/double-scroll.tsx
Normal file
258
app/(application)/coba/double-scroll.tsx
Normal file
@@ -0,0 +1,258 @@
|
||||
// File: src/screens/EventDetailScreen.tsx
|
||||
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
FlatList,
|
||||
View,
|
||||
Text,
|
||||
Image,
|
||||
StyleSheet,
|
||||
ActivityIndicator,
|
||||
} from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
// === TYPES ===
|
||||
type Participant = {
|
||||
id: number;
|
||||
name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
type EventDetail = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
location: string;
|
||||
organizer: string;
|
||||
};
|
||||
|
||||
// === KOMPONEN UTAMA ===
|
||||
const EventDetailScreen: React.FC = () => {
|
||||
const [participants, setParticipants] = useState<Participant[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [loadingMore, setLoadingMore] = useState<boolean>(false);
|
||||
|
||||
// Data event
|
||||
const event: EventDetail = {
|
||||
id: 1,
|
||||
title: "Workshop React Native & Expo",
|
||||
description:
|
||||
"Pelatihan intensif pengembangan aplikasi mobile menggunakan React Native, Expo, dan TypeScript. Cocok untuk developer tingkat menengah.",
|
||||
date: "Sabtu, 5 April 2025 | 09:00 - 16:00",
|
||||
location: "Gedung Teknologi, Jakarta Selatan",
|
||||
organizer: "DevCommunity Indonesia",
|
||||
};
|
||||
|
||||
// Simulasi API: generate data dummy
|
||||
const generateParticipants = (
|
||||
startId: number,
|
||||
count: number
|
||||
): Participant[] => {
|
||||
return Array.from({ length: count }, (_, i) => {
|
||||
const id = startId + i;
|
||||
return {
|
||||
id,
|
||||
name: `Peserta ${id}`,
|
||||
avatar: `https://i.pravatar.cc/150?img=${(id % 70) + 1}`, // 70 gambar unik
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Load data awal
|
||||
useEffect(() => {
|
||||
const loadInitial = () => {
|
||||
setTimeout(() => {
|
||||
const initialData = generateParticipants(1, 20); // 20 peserta pertama
|
||||
setParticipants(initialData);
|
||||
setLoading(false);
|
||||
}, 800);
|
||||
};
|
||||
|
||||
loadInitial();
|
||||
}, []);
|
||||
|
||||
// Load lebih banyak peserta saat scroll ke bawah
|
||||
const loadMore = () => {
|
||||
if (loadingMore || participants.length >= 200) return; // Batas 200 peserta
|
||||
|
||||
setLoadingMore(true);
|
||||
setTimeout(() => {
|
||||
const nextId = participants.length + 1;
|
||||
const newData = generateParticipants(nextId, 10); // Tambah 10 peserta
|
||||
setParticipants((prev) => [...prev, ...newData]);
|
||||
setLoadingMore(false);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// Render footer: loading indicator
|
||||
const renderFooter = () => {
|
||||
if (!loadingMore) return null;
|
||||
return (
|
||||
<View style={styles.footer}>
|
||||
<ActivityIndicator size="small" color="#007AFF" />
|
||||
<Text style={styles.loadingText}> Memuat peserta berikutnya...</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
// Render header: detail event + info jumlah peserta
|
||||
const renderHeader = () => (
|
||||
<>
|
||||
<View style={styles.headerContainer}>
|
||||
<LeftButtonCustom path={"/"} />
|
||||
<Text style={styles.title}>{event.title}</Text>
|
||||
<Text style={styles.info}>📅 {event.date}</Text>
|
||||
<Text style={styles.info}>📍 {event.location}</Text>
|
||||
<Text style={styles.info}>👤 {event.organizer}</Text>
|
||||
<Text style={styles.description}>{event.description}</Text>
|
||||
|
||||
<View style={styles.divider} />
|
||||
|
||||
<Text style={styles.sectionTitle}>
|
||||
Daftar Peserta ({participants.length})
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Sub-header tambahan jika perlu */}
|
||||
{participants.length === 0 ? (
|
||||
<Text style={styles.empty}>Belum ada peserta yang terdaftar.</Text>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
// Loading awal
|
||||
if (loading) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<ActivityIndicator size="large" color="#007AFF" style={styles.loader} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FlatList
|
||||
data={participants}
|
||||
keyExtractor={(item) => item.id.toString()}
|
||||
ListHeaderComponent={renderHeader}
|
||||
ListFooterComponent={renderFooter}
|
||||
onEndReached={loadMore}
|
||||
onEndReachedThreshold={0.5}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.participantItem}>
|
||||
<Image source={{ uri: item.avatar }} style={styles.avatar} />
|
||||
<Text style={styles.participantName}>{item.name}</Text>
|
||||
</View>
|
||||
)}
|
||||
initialNumToRender={10}
|
||||
maxToRenderPerBatch={5}
|
||||
windowSize={7}
|
||||
showsVerticalScrollIndicator={false}
|
||||
ListEmptyComponent={
|
||||
<Text style={styles.empty}>Tidak ada peserta.</Text>
|
||||
}
|
||||
/>
|
||||
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EventDetailScreen;
|
||||
|
||||
// === STYLES ===
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#f8f9fa",
|
||||
},
|
||||
loader: {
|
||||
marginTop: 20,
|
||||
},
|
||||
headerContainer: {
|
||||
padding: 16,
|
||||
backgroundColor: "#ffffff",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#e0e0e0",
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: "bold",
|
||||
color: "#1d1d1d",
|
||||
marginBottom: 8,
|
||||
},
|
||||
info: {
|
||||
fontSize: 16,
|
||||
color: "#444",
|
||||
marginBottom: 4,
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: "#666",
|
||||
lineHeight: 20,
|
||||
marginTop: 12,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: "#e0e0e0",
|
||||
marginVertical: 16,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: "600",
|
||||
color: "#1d1d1d",
|
||||
marginBottom: 12,
|
||||
},
|
||||
participantItem: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
backgroundColor: "#fff",
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#f0f0f0",
|
||||
},
|
||||
avatar: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
marginRight: 12,
|
||||
},
|
||||
participantName: {
|
||||
fontSize: 16,
|
||||
color: "#333",
|
||||
},
|
||||
footer: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: 16,
|
||||
backgroundColor: "#f8f9fa",
|
||||
},
|
||||
loadingText: {
|
||||
color: "#555",
|
||||
fontSize: 14,
|
||||
},
|
||||
empty: {
|
||||
textAlign: "center",
|
||||
color: "#999",
|
||||
fontStyle: "italic",
|
||||
padding: 16,
|
||||
backgroundColor: "#fff",
|
||||
fontSize: 14,
|
||||
},
|
||||
subHeader: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: "#f1f1f1",
|
||||
fontSize: 14,
|
||||
color: "#666",
|
||||
fontStyle: "italic",
|
||||
},
|
||||
});
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
ScrollView,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { router, Stack } from "expo-router";
|
||||
import EventDetailScreen from "./double-scroll";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
|
||||
@@ -16,7 +18,9 @@ const { width } = Dimensions.get("window");
|
||||
const HomeScreen = () => (
|
||||
<View style={styles.screen}>
|
||||
<Text style={styles.screenTitle}>Selamat Datang!</Text>
|
||||
<Text style={styles.screenText}>Ini adalah halaman utama aplikasi Anda</Text>
|
||||
<Text style={styles.screenText}>
|
||||
Ini adalah halaman utama aplikasi Anda
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
const SearchScreen = () => (
|
||||
@@ -65,9 +69,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: any) => (
|
||||
|
||||
// Main Custom Tab Navigator
|
||||
const CustomTabNavigator = () => {
|
||||
const [activeTab, setActiveTab] = React.useState(
|
||||
'home'
|
||||
);
|
||||
const [activeTab, setActiveTab] = React.useState("home");
|
||||
const [showHome, setShowHome] = React.useState(true);
|
||||
|
||||
const tabs = [
|
||||
@@ -97,13 +99,12 @@ const CustomTabNavigator = () => {
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
// Function untuk handle tab press
|
||||
const handleTabPress = (tabId: string) => {
|
||||
setActiveTab(tabId);
|
||||
setShowHome(false); // Hide home when any tab is pressed
|
||||
};
|
||||
|
||||
|
||||
// Determine which component to show
|
||||
const getActiveComponent = () => {
|
||||
if (showHome || activeTab === "home") {
|
||||
@@ -111,38 +112,46 @@ const CustomTabNavigator = () => {
|
||||
}
|
||||
// const selectedTab = tabs.find((tab) => tab.id === activeTab);
|
||||
// return selectedTab ? selectedTab.component : HomeScreen;
|
||||
return HomeScreen
|
||||
return HomeScreen;
|
||||
};
|
||||
|
||||
const ActiveComponent = getActiveComponent();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* Content Area */}
|
||||
<ScrollView>
|
||||
<View style={styles.content}>
|
||||
<ActiveComponent />
|
||||
</View>
|
||||
</ScrollView>
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Custom Tab Navigator",
|
||||
}}
|
||||
/>
|
||||
<EventDetailScreen />
|
||||
</>
|
||||
// <View style={styles.container}>
|
||||
// {/* Content Area */}
|
||||
// <ScrollView>
|
||||
// <View style={styles.content}>
|
||||
// <ActiveComponent />
|
||||
// </View>
|
||||
// </ScrollView>
|
||||
|
||||
{/* Custom Tab Bar */}
|
||||
<View style={styles.tabBar}>
|
||||
<View style={styles.tabContainer}>
|
||||
{tabs.map((e) => (
|
||||
<CustomTab
|
||||
key={e.id}
|
||||
icon={activeTab === e.id ? e.activeIcon : e.icon}
|
||||
label={e.label}
|
||||
isActive={activeTab === e.id && !showHome}
|
||||
onPress={() => {
|
||||
handleTabPress(e.id);
|
||||
router.push(e.path as any);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
// {/* Custom Tab Bar */}
|
||||
// <View style={styles.tabBar}>
|
||||
// <View style={styles.tabContainer}>
|
||||
// {tabs.map((e) => (
|
||||
// <CustomTab
|
||||
// key={e.id}
|
||||
// icon={activeTab === e.id ? e.activeIcon : e.icon}
|
||||
// label={e.label}
|
||||
// isActive={activeTab === e.id && !showHome}
|
||||
// onPress={() => {
|
||||
// handleTabPress(e.id);
|
||||
// router.push(e.path as any);
|
||||
// }}
|
||||
// />
|
||||
// ))}
|
||||
// </View>
|
||||
// </View>
|
||||
// </View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
BIN
assets/images/dummy/dummy-user.png
Normal file
BIN
assets/images/dummy/dummy-user.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 395 KiB |
3
bun.lock
3
bun.lock
@@ -16,6 +16,7 @@
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
@@ -827,6 +828,8 @@
|
||||
|
||||
"expo-camera": ["expo-camera@16.1.10", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-qoRJeSwPmMbuu0VfnQTC+q79Kt2SqTWColEImgithL9u0qUQcC55U89IfhZk55Hpt6f1DgKuDzUOG5oY+snSWg=="],
|
||||
|
||||
"expo-clipboard": ["expo-clipboard@7.1.5", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-TCANUGOxouoJXxKBW5ASJl2WlmQLGpuZGemDCL2fO5ZMl57DGTypUmagb0CVUFxDl0yAtFIcESd78UsF9o64aw=="],
|
||||
|
||||
"expo-constants": ["expo-constants@17.1.7", "", { "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA=="],
|
||||
|
||||
"expo-file-system": ["expo-file-system@18.1.11", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ=="],
|
||||
|
||||
31
components/Alert/AlertDefaultSystem.ts
Normal file
31
components/Alert/AlertDefaultSystem.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Alert } from "react-native";
|
||||
|
||||
export default function AlertDefaultSystem({
|
||||
title,
|
||||
message,
|
||||
textLeft,
|
||||
textRight,
|
||||
onPressLeft,
|
||||
onPressRight,
|
||||
}: {
|
||||
title: string;
|
||||
message: string;
|
||||
textLeft: string;
|
||||
textRight: string;
|
||||
onPressLeft?: () => void;
|
||||
onPressRight?: () => void;
|
||||
}) {
|
||||
return Alert.alert(title, message, [
|
||||
{
|
||||
style: "cancel",
|
||||
text: textLeft,
|
||||
onPress: () => onPressLeft?.(),
|
||||
},
|
||||
{
|
||||
style: "default",
|
||||
text: textRight,
|
||||
isPreferred: true,
|
||||
onPress: () => onPressRight?.(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
189
components/Badge/BadgeCustom.tsx
Normal file
189
components/Badge/BadgeCustom.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import React from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
View,
|
||||
ViewProps,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
|
||||
type BadgeVariant = "filled" | "light" | "outline" | "dot";
|
||||
type BadgeColor =
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "gray"
|
||||
| "dark";
|
||||
type BadgeSize = "xs" | "sm" | "md" | "lg";
|
||||
|
||||
interface BadgeProps extends ViewProps {
|
||||
children: React.ReactNode;
|
||||
variant?: BadgeVariant;
|
||||
color?: BadgeColor;
|
||||
size?: BadgeSize;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
radius?: number;
|
||||
fullWidth?: boolean;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
const BadgeCustom: React.FC<BadgeProps> = ({
|
||||
children,
|
||||
variant = "filled",
|
||||
color = "primary",
|
||||
size = "md",
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
radius = 50,
|
||||
fullWidth = false,
|
||||
textColor = "#fff",
|
||||
style,
|
||||
...props
|
||||
}) => {
|
||||
const colors = {
|
||||
primary: "#339AF0",
|
||||
success: "#40C057",
|
||||
warning: "#FAB005",
|
||||
danger: "#FA5252",
|
||||
gray: "#868E96",
|
||||
dark: "#212529",
|
||||
};
|
||||
|
||||
const themeColor = colors[color];
|
||||
|
||||
// Ganti bagian sizeStyles dan styles.container
|
||||
const sizeStyles = {
|
||||
xs: {
|
||||
fontSize: 10,
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 2,
|
||||
height: 18, // Dinaikkan dari 16 → 18 agar teks tidak terpotong
|
||||
lineHeight: 10, // 👈 Penting: match fontSize agar kontrol vertikal lebih baik
|
||||
},
|
||||
sm: {
|
||||
fontSize: 11,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
height: 20,
|
||||
lineHeight: 11,
|
||||
},
|
||||
md: {
|
||||
fontSize: 12,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
height: 24,
|
||||
lineHeight: 12,
|
||||
},
|
||||
lg: {
|
||||
fontSize: 14,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
height: 30,
|
||||
lineHeight: 14,
|
||||
},
|
||||
};
|
||||
|
||||
const currentSize = sizeStyles[size];
|
||||
|
||||
let variantStyles: ViewStyle & { text: TextStyle } = {
|
||||
backgroundColor: themeColor,
|
||||
borderColor: themeColor,
|
||||
borderWidth: 1,
|
||||
borderRadius: radius,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
text: { color: textColor, fontWeight: "600" },
|
||||
};
|
||||
|
||||
switch (variant) {
|
||||
case "light":
|
||||
variantStyles.backgroundColor = `${themeColor}20`;
|
||||
variantStyles.text.color = themeColor;
|
||||
break;
|
||||
case "outline":
|
||||
variantStyles.backgroundColor = "transparent";
|
||||
variantStyles.text.color = themeColor;
|
||||
break;
|
||||
case "dot":
|
||||
variantStyles.backgroundColor = themeColor;
|
||||
variantStyles.paddingHorizontal = 0;
|
||||
variantStyles.paddingVertical = 0;
|
||||
variantStyles.height = currentSize.fontSize * 2;
|
||||
variantStyles.width = currentSize.fontSize * 2;
|
||||
variantStyles.borderRadius = currentSize.fontSize;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (variant === "dot") {
|
||||
return (
|
||||
<View
|
||||
style={[variantStyles, fullWidth && styles.fullWidth, style]}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
variantStyles,
|
||||
currentSize,
|
||||
{ borderRadius: radius },
|
||||
fullWidth && styles.fullWidth,
|
||||
style,
|
||||
]}
|
||||
{...props}
|
||||
>
|
||||
{leftIcon && <View style={styles.iconContainer}>{leftIcon}</View>}
|
||||
<Text
|
||||
style={[
|
||||
styles.text,
|
||||
variantStyles.text,
|
||||
{ fontSize: currentSize.fontSize },
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
{rightIcon && <View style={styles.iconContainer}>{rightIcon}</View>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignSelf: "flex-start",
|
||||
flexDirection: "row",
|
||||
alignItems: "center", // Vertikal center anak-anak (termasuk teks)
|
||||
justifyContent: "center", // Horizontal center
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
minWidth: 20,
|
||||
borderRadius: 6,
|
||||
// ❌ Jangan gunakan `height` fix di sini — kita override per size
|
||||
},
|
||||
text: {
|
||||
fontWeight: "600",
|
||||
textAlign: "center",
|
||||
// ❌ Hapus marginHorizontal jika mengganggu alignment
|
||||
// marginHorizontal: 2, // Opsional, bisa dihapus atau dikurangi
|
||||
includeFontPadding: false, // 👈 Ini penting untuk Android!
|
||||
padding: 0, // Bersihkan padding tambahan dari font
|
||||
},
|
||||
iconContainer: {
|
||||
marginHorizontal: 2, // Lebih kecil dari sebelumnya agar tidak ganggu ukuran kecil
|
||||
},
|
||||
fullWidth: {
|
||||
width: "100%",
|
||||
alignSelf: "stretch",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
|
||||
export default BadgeCustom;
|
||||
@@ -1,57 +1,79 @@
|
||||
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";
|
||||
import {
|
||||
PADDING_MEDIUM,
|
||||
PADDING_SMALL
|
||||
} from "@/constants/constans-value";
|
||||
import { Href, router } from "expo-router";
|
||||
import {
|
||||
StyleProp,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
ViewStyle
|
||||
} from "react-native";
|
||||
|
||||
interface BaseBoxProps {
|
||||
children: React.ReactNode;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
href?: Href;
|
||||
onPress?: () => void;
|
||||
marginBottom?: number;
|
||||
padding?: number;
|
||||
paddingTop?: number;
|
||||
paddingBottom?: number;
|
||||
paddingInline?: number;
|
||||
paddingBlock?: number;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export default function BaseBox({
|
||||
children,
|
||||
style,
|
||||
href,
|
||||
onPress,
|
||||
marginBottom = PADDING_MEDIUM,
|
||||
paddingBlock = PADDING_EXTRA_SMALL,
|
||||
paddingBlock = PADDING_MEDIUM,
|
||||
paddingInline = PADDING_SMALL,
|
||||
paddingTop = PADDING_MEDIUM,
|
||||
paddingBottom = PADDING_MEDIUM,
|
||||
backgroundColor = AccentColor.darkblue,
|
||||
}: BaseBoxProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{onPress ? (
|
||||
<TouchableHighlight
|
||||
onPress={onPress}
|
||||
{onPress || href ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={href ? () => router.navigate(href) : onPress}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: AccentColor.darkblue,
|
||||
backgroundColor,
|
||||
borderColor: AccentColor.blue,
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
marginBottom,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
// activeOpacity={0.7}
|
||||
>
|
||||
<View>{children}</View>
|
||||
</TouchableHighlight>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
backgroundColor: AccentColor.darkblue,
|
||||
backgroundColor,
|
||||
borderColor: AccentColor.blue,
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
marginBottom,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
|
||||
20
components/Box/BoxWithHeaderInformation.tsx
Normal file
20
components/Box/BoxWithHeaderInformation.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Href } from "expo-router";
|
||||
import BaseBox from "./BaseBox";
|
||||
|
||||
export default function BoxWithHeaderSection({
|
||||
children,
|
||||
href,
|
||||
onPress,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
href?: Href;
|
||||
onPress?: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox href={href} onPress={onPress} style={{ paddingTop: 5 }}>
|
||||
{children}
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
13
components/Button/DotButton.tsx
Normal file
13
components/Button/DotButton.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
|
||||
export default function DotButton({ onPress }: { onPress: () => void }) {
|
||||
return (
|
||||
<Ionicons
|
||||
onPress={onPress}
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
);
|
||||
}
|
||||
133
components/Checkbox/CheckboxCustom.tsx
Normal file
133
components/Checkbox/CheckboxCustom.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { MaterialIcons } from "@expo/vector-icons"; // Bisa diganti dengan ikon lain
|
||||
import React, { useContext } from "react";
|
||||
import { Animated, Text, TouchableOpacity, View } from "react-native";
|
||||
import { checkboxStyles } from "./checkbox-styles";
|
||||
|
||||
// Context untuk Group
|
||||
interface CheckboxGroupContextType {
|
||||
value: (string | number)[];
|
||||
onChange: (value: (string | number)[]) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CheckboxGroupContext =
|
||||
React.createContext<CheckboxGroupContextType | null>(null);
|
||||
|
||||
// Tipe props
|
||||
// Tambahkan prop baru: groupValueKey
|
||||
interface CheckboxProps {
|
||||
label?: string;
|
||||
description?: string;
|
||||
error?: string;
|
||||
value?: boolean; // controlled value (untuk standalone)
|
||||
onChange?: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
size?: number;
|
||||
color?: string;
|
||||
style?: object;
|
||||
component?: React.ReactNode;
|
||||
// Prop tambahan untuk Group
|
||||
valueKey?: string | number; // nilai unik untuk identifikasi di group
|
||||
}
|
||||
|
||||
const CheckboxCustom: React.FC<CheckboxProps> = ({
|
||||
label,
|
||||
description,
|
||||
error,
|
||||
value: controlledValue,
|
||||
onChange,
|
||||
disabled: propDisabled,
|
||||
size = 20,
|
||||
color = AccentColor.softblue,
|
||||
style,
|
||||
component,
|
||||
valueKey,
|
||||
}) => {
|
||||
// const [uncontrolledChecked, setUncontrolledChecked] = useState(false);
|
||||
// const isChecked = controlledValue ?? uncontrolledChecked;
|
||||
// const scaleValue = new Animated.Value(isChecked ? 1 : 0);
|
||||
|
||||
const group = useContext(CheckboxGroupContext);
|
||||
const isInsideGroup = !!group && valueKey !== undefined;
|
||||
|
||||
// Jika di dalam group, gunakan logika group
|
||||
const isChecked = isInsideGroup
|
||||
? group.value.includes(valueKey!)
|
||||
: controlledValue ?? false;
|
||||
|
||||
const disabled = propDisabled || (isInsideGroup && group.disabled);
|
||||
|
||||
const scaleValue = new Animated.Value(isChecked ? 1 : 0);
|
||||
|
||||
const toggle = () => {
|
||||
if (disabled) return;
|
||||
|
||||
if (isInsideGroup) {
|
||||
const newValue = isChecked
|
||||
? group.value.filter((v) => v !== valueKey)
|
||||
: [...group.value, valueKey!];
|
||||
group.onChange(newValue);
|
||||
} else if (onChange) {
|
||||
onChange(!controlledValue);
|
||||
}
|
||||
};
|
||||
|
||||
const styles = checkboxStyles({
|
||||
size,
|
||||
color,
|
||||
disabled: disabled as boolean,
|
||||
error: !!error,
|
||||
});
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
activeOpacity={disabled ? 1 : 0.7}
|
||||
onPress={toggle}
|
||||
style={[styles.container, style]}
|
||||
disabled={disabled}
|
||||
>
|
||||
<View style={styles.innerContainer}>
|
||||
<View style={[styles.box, isChecked && !disabled && styles.checked]}>
|
||||
{isChecked && (
|
||||
<Animated.View
|
||||
style={{
|
||||
transform: [
|
||||
{
|
||||
scale: scaleValue.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 1],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}}
|
||||
>
|
||||
<MaterialIcons name="check" size={size * 0.6} color="#fff" />
|
||||
</Animated.View>
|
||||
)}
|
||||
</View>
|
||||
{component}
|
||||
{(label || description) && (
|
||||
<View style={styles.labelWrapper}>
|
||||
{label ? (
|
||||
<Text style={styles.label} numberOfLines={1}>
|
||||
{label}
|
||||
</Text>
|
||||
) : null}
|
||||
{description ? (
|
||||
<Text style={styles.description} numberOfLines={2}>
|
||||
{description}
|
||||
</Text>
|
||||
) : null}
|
||||
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxCustom;
|
||||
|
||||
// Export context agar bisa digunakan
|
||||
export { CheckboxGroupContext };
|
||||
75
components/Checkbox/CheckboxGroup.tsx
Normal file
75
components/Checkbox/CheckboxGroup.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { View, Text, StyleSheet } from "react-native";
|
||||
import { CheckboxGroupContext } from "./CheckboxCustom";
|
||||
|
||||
interface CheckboxGroupProps {
|
||||
value?: (string | number)[];
|
||||
onChange?: (values: (string | number)[]) => void;
|
||||
defaultValue?: (string | number)[];
|
||||
label?: string;
|
||||
description?: string;
|
||||
error?: string;
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
style?: object;
|
||||
}
|
||||
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
|
||||
value: controlledValue,
|
||||
onChange,
|
||||
defaultValue = [],
|
||||
label,
|
||||
description,
|
||||
error,
|
||||
disabled = false,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const [uncontrolledValue, setUncontrolledValue] =
|
||||
useState<(string | number)[]>(defaultValue);
|
||||
|
||||
const value = controlledValue ?? uncontrolledValue;
|
||||
const handleChange = onChange ?? setUncontrolledValue;
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({ value, onChange: handleChange, disabled }),
|
||||
[value, handleChange, disabled]
|
||||
);
|
||||
|
||||
return (
|
||||
<CheckboxGroupContext.Provider value={contextValue}>
|
||||
<View style={[styles.container, style]}>
|
||||
{label ? <Text style={styles.label}>{label}</Text> : null}
|
||||
{description ? (
|
||||
<Text style={styles.description}>{description}</Text>
|
||||
) : null}
|
||||
{children}
|
||||
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
||||
</View>
|
||||
</CheckboxGroupContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
gap: 8,
|
||||
},
|
||||
label: {
|
||||
fontSize: 16,
|
||||
fontWeight: "600",
|
||||
color: "#f8f9fa",
|
||||
marginBottom: 4,
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: "#ced4da",
|
||||
marginBottom: 8,
|
||||
},
|
||||
errorText: {
|
||||
color: "#e03131",
|
||||
fontSize: 14,
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export default CheckboxGroup;
|
||||
|
||||
61
components/Checkbox/checkbox-styles.tsx
Normal file
61
components/Checkbox/checkbox-styles.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
export const checkboxStyles = (props: {
|
||||
size: number;
|
||||
color: string;
|
||||
disabled: boolean;
|
||||
error: boolean;
|
||||
}) =>
|
||||
StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
alignItems: "flex-start",
|
||||
// marginBottom: 12,
|
||||
},
|
||||
innerContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
},
|
||||
box: {
|
||||
width: props.size,
|
||||
height: props.size,
|
||||
borderRadius: 6,
|
||||
borderWidth: 2,
|
||||
borderColor: props.error
|
||||
? "#fff"
|
||||
: props.disabled
|
||||
? "#ced4da"
|
||||
: props.color,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 10,
|
||||
},
|
||||
checked: {
|
||||
backgroundColor: props.color,
|
||||
borderColor: props.color,
|
||||
},
|
||||
checkIcon: {
|
||||
color: MainColor.white,
|
||||
fontWeight: "bold",
|
||||
fontSize: props.size * 0.6,
|
||||
},
|
||||
labelWrapper: {
|
||||
flex: 1,
|
||||
},
|
||||
label: {
|
||||
fontSize: props.size * 0.6,
|
||||
color: props.disabled ? MainColor.disabled : MainColor.white,
|
||||
fontWeight: "500",
|
||||
},
|
||||
description: {
|
||||
fontSize: props.size * 0.5,
|
||||
color: props.disabled ? MainColor.disabled : MainColor.white,
|
||||
marginTop: 2,
|
||||
},
|
||||
errorText: {
|
||||
color: MainColor.red,
|
||||
fontSize: props.size * 0.5,
|
||||
marginTop: 2,
|
||||
},
|
||||
});
|
||||
44
components/Container/CircleContainer.tsx
Normal file
44
components/Container/CircleContainer.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import React from "react";
|
||||
import { StyleSheet, TextInput, View } from "react-native";
|
||||
|
||||
interface CircularInputProps {
|
||||
value: string | number;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange }) => {
|
||||
return (
|
||||
<View style={styles.circleContainer}>
|
||||
<TextInput
|
||||
value={String(value)}
|
||||
onChangeText={onChange}
|
||||
style={styles.input}
|
||||
keyboardType="numeric"
|
||||
maxLength={2} // Batasan maksimal karakter
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
circleContainer: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 40, // Setiap setengah dari lebar/tinggi
|
||||
borderWidth: 2,
|
||||
borderColor: MainColor.yellow, // Warna kuning
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
input: {
|
||||
color: MainColor.yellow, // Warna kuning
|
||||
fontSize: 24,
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
padding: 0,
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
});
|
||||
|
||||
export default CircularInput;
|
||||
25
components/Divider/Divider.tsx
Normal file
25
components/Divider/Divider.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Divider({
|
||||
color = AccentColor.blue,
|
||||
size = 1,
|
||||
marginTop= 12,
|
||||
marginBottom= 12,
|
||||
}: {
|
||||
color?: string;
|
||||
size?: number;
|
||||
marginTop?: number;
|
||||
marginBottom?: number;
|
||||
}) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
borderTopColor: color,
|
||||
borderTopWidth: size,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -9,20 +9,20 @@ import {
|
||||
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { DRAWER_HEIGHT } from "@/constants/constans-value";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
interface DrawerCustomProps {
|
||||
children?: React.ReactNode;
|
||||
height?: number;
|
||||
height?: number | "auto";
|
||||
isVisible: boolean;
|
||||
drawerAnim?: Animated.Value;
|
||||
closeDrawer: () => void;
|
||||
// openLogoutAlert: () => void;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param drawerAnim
|
||||
*
|
||||
* @param drawerAnim
|
||||
* @example const drawerAnim = useRef(new Animated.Value(DRAWER_HEIGHT)).current; // mulai di luar bawah layar
|
||||
*/
|
||||
export default function DrawerCustom({
|
||||
@@ -34,7 +34,9 @@ export default function DrawerCustom({
|
||||
}: // openLogoutAlert,
|
||||
DrawerCustomProps) {
|
||||
const drawerAnima = useRef(
|
||||
new Animated.Value(height || DRAWER_HEIGHT)
|
||||
new Animated.Value(
|
||||
height === "auto" ? DRAWER_HEIGHT : height || DRAWER_HEIGHT
|
||||
)
|
||||
).current;
|
||||
// Efek untuk handle open/close drawer
|
||||
useEffect(() => {
|
||||
@@ -46,7 +48,7 @@ DrawerCustomProps) {
|
||||
}).start();
|
||||
} else {
|
||||
Animated.timing(drawerAnima, {
|
||||
toValue: height || DRAWER_HEIGHT,
|
||||
toValue: height === "auto" ? DRAWER_HEIGHT : height || DRAWER_HEIGHT,
|
||||
duration: 300,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
@@ -99,7 +101,7 @@ DrawerCustomProps) {
|
||||
style={[
|
||||
styles.drawer,
|
||||
{
|
||||
height: height || DRAWER_HEIGHT,
|
||||
height: height === "auto" ? "auto" : height || DRAWER_HEIGHT,
|
||||
transform: [{ translateY: drawerAnima }],
|
||||
},
|
||||
]}
|
||||
@@ -110,34 +112,7 @@ DrawerCustomProps) {
|
||||
/>
|
||||
|
||||
{children}
|
||||
|
||||
{/* <TouchableOpacity
|
||||
style={styles.menuItem}
|
||||
onPress={() => {
|
||||
alert("Pilihan 1 diklik");
|
||||
closeDrawer();
|
||||
}}
|
||||
>
|
||||
<Text>Menu Item 1</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.menuItem}
|
||||
onPress={() => {
|
||||
alert("Pilihan 2 diklik");
|
||||
closeDrawer();
|
||||
}}
|
||||
>
|
||||
<Text>Menu Item 2</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.menuItem}
|
||||
onPress={() => alert("Logout via Alert bawaan")}
|
||||
>
|
||||
<Text style={{ color: "red" }}>Keluar</Text>
|
||||
</TouchableOpacity> */}
|
||||
{height === "auto" && <SafeAreaView edges={["bottom"]} />}
|
||||
</Animated.View>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -2,13 +2,26 @@ import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||
import { IMenuDrawerItem } from "../_Interface/types";
|
||||
import { Href } from "expo-router";
|
||||
|
||||
const MenuDrawerDynamicGrid = ({ data, columns = 3, onPressItem }: any) => {
|
||||
type IMenuDrawerItemProps = {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
path?: Href;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
interface MenuDrawerDynamicGridProps {
|
||||
data: IMenuDrawerItemProps[];
|
||||
columns?: number;
|
||||
onPressItem?: (item: IMenuDrawerItemProps) => void;
|
||||
}
|
||||
const MenuDrawerDynamicGrid = ({ data, columns = 4, onPressItem }: MenuDrawerDynamicGridProps) => {
|
||||
const numColumns = columns;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{data.map((item: IMenuDrawerItem, index: any) => (
|
||||
{data.map((item: IMenuDrawerItemProps, index: any) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={[styles.itemContainer, { flexBasis: `${100 / numColumns}%` }]}
|
||||
|
||||
@@ -6,6 +6,7 @@ import ButtonCustom from "../Button/ButtonCustom";
|
||||
interface ButtonData {
|
||||
id: string | number;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ScrollableCustomProps {
|
||||
@@ -27,7 +28,7 @@ const ScrollableCustom = ({
|
||||
style={styles.scrollView}
|
||||
>
|
||||
{data.map((item) => {
|
||||
const isActive = activeId === item.id;
|
||||
const isActive = activeId === item.value;
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
@@ -48,6 +49,9 @@ export default ScrollableCustom;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scrollView: {
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 50,
|
||||
padding: 5,
|
||||
// maxHeight: 50,
|
||||
},
|
||||
buttonContainer: {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
TEXT_SIZE_LARGE,
|
||||
TEXT_SIZE_MEDIUM,
|
||||
TEXT_SIZE_SMALL,
|
||||
TEXT_SIZE_XLARGE,
|
||||
} from "@/constants/constans-value";
|
||||
import React from "react";
|
||||
import {
|
||||
@@ -21,7 +22,7 @@ interface TextCustomProps {
|
||||
style?: StyleProp<TextStyle>;
|
||||
bold?: boolean;
|
||||
semiBold?: boolean;
|
||||
size?: "default" | "large" | "small";
|
||||
size?: "default" | "large" | "small" | "xlarge";
|
||||
color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
|
||||
align?: TextAlign; // Prop untuk alignment
|
||||
truncate?: boolean | number;
|
||||
@@ -51,6 +52,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
||||
|
||||
// Size
|
||||
if (size === "large") selectedStyles.push(styles.large);
|
||||
else if (size === "xlarge") selectedStyles.push(styles.xlarge);
|
||||
else if (size === "small") selectedStyles.push(styles.small);
|
||||
|
||||
// Color
|
||||
@@ -113,11 +115,14 @@ export const styles = StyleSheet.create({
|
||||
fontFamily: "Poppins-SemiBold",
|
||||
fontWeight: "500",
|
||||
},
|
||||
small: {
|
||||
fontSize: TEXT_SIZE_SMALL,
|
||||
},
|
||||
large: {
|
||||
fontSize: TEXT_SIZE_LARGE,
|
||||
},
|
||||
small: {
|
||||
fontSize: TEXT_SIZE_SMALL,
|
||||
xlarge: {
|
||||
fontSize: TEXT_SIZE_XLARGE,
|
||||
},
|
||||
yellow: {
|
||||
color: MainColor.yellow,
|
||||
|
||||
14
components/_Icon/IconContribution.tsx
Normal file
14
components/_Icon/IconContribution.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconContribution({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<Ionicons
|
||||
size={ICON_SIZE_SMALL}
|
||||
name="extension-puzzle"
|
||||
color={color || "white"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
components/_Icon/IconEdit.tsx
Normal file
10
components/_Icon/IconEdit.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
|
||||
export default function IconEdit() {
|
||||
return (
|
||||
<>
|
||||
<FontAwesome5 name="edit" size={ICON_SIZE_SMALL} color="white" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
9
components/_Icon/IconHistory.tsx
Normal file
9
components/_Icon/IconHistory.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
|
||||
export default function IconHistory({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<FontAwesome5 size={20} name="history" color={color} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
components/_Icon/IconHome.tsx
Normal file
10
components/_Icon/IconHome.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconHome({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<Ionicons name="home" size={ICON_SIZE_SMALL} color={color || "white"} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
12
components/_Icon/IconStatus.tsx
Normal file
12
components/_Icon/IconStatus.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconStatus({ color }: { color?: string }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
size={ICON_SIZE_SMALL}
|
||||
name="checklist-rtl"
|
||||
color={color || "white"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
7
components/_Icon/index.ts
Normal file
7
components/_Icon/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import IconContribution from "./IconContribution";
|
||||
import IconEdit from "./IconEdit";
|
||||
import IconHistory from "./IconHistory";
|
||||
import IconHome from "./IconHome";
|
||||
import IconStatus from "./IconStatus";
|
||||
|
||||
export { IconContribution, IconEdit, IconHistory, IconHome, IconStatus };
|
||||
@@ -0,0 +1,50 @@
|
||||
import { ImageSourcePropType, View } from "react-native";
|
||||
import Grid from "../Grid/GridCustom";
|
||||
import AvatarCustom from "../Image/AvatarCustom";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
import Divider from "../Divider/Divider"
|
||||
|
||||
const AvatarUsernameAndOtherComponent = ({
|
||||
avatarHref,
|
||||
avatar,
|
||||
name,
|
||||
rightComponent,
|
||||
withBottomLine = false,
|
||||
}: {
|
||||
avatarHref?: string;
|
||||
avatar?: ImageSourcePropType;
|
||||
name?: string;
|
||||
rightComponent?: React.ReactNode;
|
||||
withBottomLine?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Grid containerStyle={{ zIndex: 10 }}>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom source={avatar} href={avatarHref as any} />
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={rightComponent ? 6 : 10}
|
||||
style={{ justifyContent: "center" }}
|
||||
>
|
||||
<TextCustom truncate bold>
|
||||
{name || "Username"}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
{rightComponent && (
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
{rightComponent}
|
||||
</Grid.Col>
|
||||
)}
|
||||
</Grid>
|
||||
{withBottomLine && <Divider marginTop={0} />}
|
||||
<View>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AvatarUsernameAndOtherComponent;
|
||||
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image } from "expo-image";
|
||||
import { StyleSheet } from "react-native";
|
||||
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function DummyLandscapeImage() {
|
||||
return (
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/preview-image/1");
|
||||
}}
|
||||
>
|
||||
<Image source={DUMMY_IMAGE.background} style={styles.backgroundImage} />
|
||||
</ClickableCustom>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
backgroundImage: {
|
||||
width: "100%",
|
||||
height: 200, // Tinggi background sesuai kebutuhan
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: 6,
|
||||
overflow: "hidden",
|
||||
borderWidth: 1,
|
||||
borderColor: AccentColor.blue,
|
||||
backgroundColor: "white",
|
||||
},
|
||||
});
|
||||
39
components/_ShareComponent/SearchInput.tsx
Normal file
39
components/_ShareComponent/SearchInput.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import TextInputCustom from "../TextInput/TextInputCustom";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { StyleProp, ViewStyle, TextStyle } from "react-native";
|
||||
|
||||
interface SearchInputProps {
|
||||
placeholder?: string;
|
||||
onPress?: () => void;
|
||||
iconLeft?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
style?: StyleProp<TextStyle>;
|
||||
}
|
||||
export default function SearchInput({
|
||||
placeholder,
|
||||
onPress,
|
||||
iconLeft,
|
||||
iconRight,
|
||||
containerStyle = { marginBottom: 0 },
|
||||
style,
|
||||
...props
|
||||
}: SearchInputProps) {
|
||||
return (
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder={placeholder}
|
||||
borderRadius={50}
|
||||
containerStyle={containerStyle}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -3,12 +3,12 @@ import React from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface SpacingProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
}
|
||||
|
||||
const Spacing: React.FC<SpacingProps> = ({ width = 20, height = 20 }) => {
|
||||
return <View style={{ height, width }} />;
|
||||
return <View style={{ height: height as any, width: width as any }} />;
|
||||
};
|
||||
|
||||
export default Spacing;
|
||||
|
||||
15
components/_ShareComponent/TabBarBackground.tsx
Normal file
15
components/_ShareComponent/TabBarBackground.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function TabBarBackground() {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: AccentColor.blue,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -20,15 +20,23 @@ interface ViewWrapperProps {
|
||||
headerComponent?: React.ReactNode;
|
||||
footerComponent?: React.ReactNode;
|
||||
floatingButton?: React.ReactNode;
|
||||
hideFooter?: boolean;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hideFooter
|
||||
* @returns meneyembunyikan footer ketika menggunakan tabs (misal: bottom tab)
|
||||
*/
|
||||
|
||||
const ViewWrapper = ({
|
||||
children,
|
||||
withBackground = false,
|
||||
headerComponent,
|
||||
footerComponent,
|
||||
floatingButton,
|
||||
hideFooter = false,
|
||||
style,
|
||||
}: ViewWrapperProps) => {
|
||||
const assetBackground = require("../../assets/images/main-background.png");
|
||||
@@ -78,10 +86,12 @@ const ViewWrapper = ({
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
) : (
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
/>
|
||||
hideFooter ? null : (
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Floating Component (misal: FAB) */}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// Alert
|
||||
import AlertCustom from "./Alert/AlertCustom";
|
||||
import AlertDefaultSystem from "./Alert/AlertDefaultSystem";
|
||||
// Button
|
||||
import LeftButtonCustom from "./Button/BackButton";
|
||||
import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
||||
import ButtonCustom from "./Button/ButtonCustom";
|
||||
import DotButton from "./Button/DotButton";
|
||||
import FloatingButton from "./Button/FloatingButton";
|
||||
// Badge
|
||||
import BadgeCustom from "./Badge/BadgeCustom";
|
||||
// Container
|
||||
import CircleContainer from "./Container/CircleContainer";
|
||||
// Checkbox
|
||||
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
||||
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
||||
// Drawer
|
||||
import DrawerCustom from "./Drawer/DrawerCustom";
|
||||
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
|
||||
// ShareComponent
|
||||
import Spacing from "./_ShareComponent/Spacing";
|
||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||
// Text
|
||||
import TextCustom from "./Text/TextCustom";
|
||||
// TextInput
|
||||
@@ -21,6 +28,7 @@ import Grid from "./Grid/GridCustom";
|
||||
// Box
|
||||
import BaseBox from "./Box/BaseBox";
|
||||
import BoxButtonOnFooter from "./Box/BoxButtonOnFooter";
|
||||
import BoxWithHeaderSection from "./Box/BoxWithHeaderInformation";
|
||||
import InformationBox from "./Box/InformationBox";
|
||||
// Stack
|
||||
import StackCustom from "./Stack/StackCustom";
|
||||
@@ -30,6 +38,7 @@ import SelectCustom from "./Select/SelectCustom";
|
||||
import AvatarCustom from "./Image/AvatarCustom";
|
||||
import LandscapeFrameUploaded from "./Image/LandscapeFrameUploaded";
|
||||
// Divider
|
||||
import Divider from "./Divider/Divider";
|
||||
import DividerCustom from "./Divider/DividerCustom";
|
||||
// Map
|
||||
import MapCustom from "./Map/MapCustom";
|
||||
@@ -39,33 +48,65 @@ import CenterCustom from "./Center/CenterCustom";
|
||||
import ClickableCustom from "./Clickable/ClickableCustom";
|
||||
// Scroll
|
||||
import ScrollableCustom from "./Scroll/ScrollCustom";
|
||||
// ShareComponent
|
||||
import AvatarUsernameAndOtherComponent from "./_ShareComponent/AvataraAndOtherHeaderComponent";
|
||||
import Spacing from "./_ShareComponent/Spacing";
|
||||
import TabBarBackground from "./_ShareComponent/TabBarBackground";
|
||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||
import SearchInput from "./_ShareComponent/SearchInput";
|
||||
import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
|
||||
|
||||
export {
|
||||
AlertCustom,
|
||||
AlertDefaultSystem,
|
||||
// Image
|
||||
AvatarCustom,
|
||||
LandscapeFrameUploaded,
|
||||
// ShareComponent
|
||||
AvatarUsernameAndOtherComponent,
|
||||
// Button
|
||||
LeftButtonCustom as BackButton,
|
||||
// Box
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
BoxWithHeaderSection,
|
||||
ButtonCenteredOnly,
|
||||
InformationBox,
|
||||
LeftButtonCustom as BackButton,
|
||||
// Button
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
// Badge
|
||||
BadgeCustom,
|
||||
// Center
|
||||
CenterCustom,
|
||||
// Checkbox
|
||||
CheckboxCustom,
|
||||
CheckboxGroup,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// Container
|
||||
CircleContainer,
|
||||
// Divider
|
||||
Divider,
|
||||
DividerCustom,
|
||||
// Drawer
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
FloatingButton,
|
||||
// Grid
|
||||
Grid,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
// Map
|
||||
MapCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
// Scroll
|
||||
ScrollableCustom,
|
||||
// Select
|
||||
SelectCustom,
|
||||
// ShareComponent
|
||||
SearchInput,
|
||||
DummyLandscapeImage,
|
||||
Spacing,
|
||||
// Stack
|
||||
StackCustom,
|
||||
TabBarBackground,
|
||||
// TextArea
|
||||
TextAreaCustom,
|
||||
// Text
|
||||
@@ -74,12 +115,4 @@ export {
|
||||
TextInputCustom,
|
||||
// ViewWrapper
|
||||
ViewWrapper,
|
||||
// Divider
|
||||
DividerCustom,
|
||||
// Center
|
||||
CenterCustom,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// Scroll
|
||||
ScrollableCustom,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ export {
|
||||
TEXT_SIZE_SMALL,
|
||||
TEXT_SIZE_MEDIUM,
|
||||
TEXT_SIZE_LARGE,
|
||||
TEXT_SIZE_XLARGE,
|
||||
ICON_SIZE_SMALL,
|
||||
ICON_SIZE_MEDIUM,
|
||||
DRAWER_HEIGHT,
|
||||
@@ -27,6 +28,7 @@ const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
|
||||
const TEXT_SIZE_SMALL = 12;
|
||||
const TEXT_SIZE_MEDIUM = 14;
|
||||
const TEXT_SIZE_LARGE = 16;
|
||||
const TEXT_SIZE_XLARGE = 18;
|
||||
|
||||
// Icon Size
|
||||
const ICON_SIZE_BUTTON = 18
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const DUMMY_IMAGE = {
|
||||
avatar: require("@/assets/images/dummy/dummy-avatar.png"),
|
||||
avatar: require("@/assets/images/dummy/dummy-user.png"),
|
||||
background: require("@/assets/images/dummy/dummy-image-background.jpg"),
|
||||
};
|
||||
|
||||
|
||||
6
lib/dummy-data/_master/status.tsx
Normal file
6
lib/dummy-data/_master/status.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
export const masterStatus = [
|
||||
{ value: "publish", label: "Publish" },
|
||||
{ value: "review", label: "Review" },
|
||||
{ value: "draft", label: "Draft" },
|
||||
{ value: "reject", label: "Reject" },
|
||||
];
|
||||
@@ -23,6 +23,7 @@
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
|
||||
@@ -32,11 +32,12 @@ export default function LoginView() {
|
||||
|
||||
// router.navigate("/verification");
|
||||
// router.navigate(`/(application)/(user)/profile/${id}`);
|
||||
// router.navigate("/(application)/(user)/home");
|
||||
router.navigate("/(application)/(user)/home");
|
||||
// router.navigate(`/(application)/profile/${id}/edit`);
|
||||
// router.navigate(`/(application)/(user)/portofolio/${id}`)
|
||||
// router.navigate(`/(application)/(image)/preview-image/${id}`);
|
||||
router.replace("/(application)/(user)/event/(tabs)");
|
||||
// router.replace("/(application)/(user)/event/(tabs)");
|
||||
// router.replace("/(application)/coba");
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
54
screens/Collaboration/BoxDetailSection.tsx
Normal file
54
screens/Collaboration/BoxDetailSection.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
|
||||
export default function Collaboration_BoxDetailSection({ id }: { id: string }) {
|
||||
return (
|
||||
<>
|
||||
<BoxWithHeaderSection>
|
||||
<StackCustom>
|
||||
<AvatarUsernameAndOtherComponent />
|
||||
<TextCustom align="center" bold size="large">
|
||||
Judul Proyek {id}
|
||||
</TextCustom>
|
||||
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.title}</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom>{item.value}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Industri",
|
||||
value: "Pilihan Industri",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value: "Deskripsi Proyek",
|
||||
},
|
||||
{
|
||||
title: "Tujuan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
},
|
||||
{
|
||||
title: "Keuntungan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
},
|
||||
];
|
||||
60
screens/Collaboration/BoxPublishSection.tsx
Normal file
60
screens/Collaboration/BoxPublishSection.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { Href } from "expo-router";
|
||||
|
||||
function Collaboration_BoxPublishSection({
|
||||
id,
|
||||
title,
|
||||
username,
|
||||
description,
|
||||
href,
|
||||
|
||||
// Avatar
|
||||
sourceAvatar,
|
||||
rightComponentAvatar,
|
||||
}: {
|
||||
id: string;
|
||||
title?: string;
|
||||
username?: string;
|
||||
description?: string;
|
||||
href: Href;
|
||||
|
||||
// Avatar
|
||||
sourceAvatar?: string;
|
||||
rightComponentAvatar?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BoxWithHeaderSection href={href}>
|
||||
<StackCustom gap={0}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${id}`}
|
||||
name={username || "Username"}
|
||||
rightComponent={rightComponentAvatar}
|
||||
avatar={sourceAvatar as any}
|
||||
withBottomLine
|
||||
/>
|
||||
|
||||
<StackCustom>
|
||||
<TextCustom truncate={2} size="large" bold align="center">
|
||||
{title || "Lorem ipsum dolor sit"}
|
||||
</TextCustom>
|
||||
<TextCustom truncate={2}>
|
||||
{description ||
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa, obcaecati quia suscipit numquam, voluptates commodi porro impedit natus quos doloremque!"}
|
||||
</TextCustom>
|
||||
{/* <TextCustom bold size="small" >
|
||||
2 Partisipan
|
||||
</TextCustom> */}
|
||||
</StackCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Collaboration_BoxPublishSection;
|
||||
69
screens/Collaboration/ProjectMainSelectedSection.tsx
Normal file
69
screens/Collaboration/ProjectMainSelectedSection.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
CheckboxCustom,
|
||||
CheckboxGroup,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Collaboration_ProjectMainSelectedSection({
|
||||
selected,
|
||||
setSelected,
|
||||
setOpenDrawerParticipant,
|
||||
}: {
|
||||
selected: (string | number)[];
|
||||
setSelected: (value: (string | number)[]) => void;
|
||||
setOpenDrawerParticipant: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<StackCustom>
|
||||
<TextCustom size="default" color="red" bold>
|
||||
*{" "}
|
||||
<TextCustom size="small" semiBold>
|
||||
Pilih user yang akan menjadi tim proyek anda
|
||||
</TextCustom>
|
||||
</TextCustom>
|
||||
|
||||
<CheckboxGroup value={selected} onChange={setSelected}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<View key={index}>
|
||||
<Grid key={index}>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CheckboxCustom valueKey={`user-${index}`} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<AvatarCustom />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom bold truncate>
|
||||
Username
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="notes"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
onPress={() => setOpenDrawerParticipant(true)}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
))}
|
||||
</CheckboxGroup>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
);
|
||||
}
|
||||
115
screens/Event/AlertButtonStatusSection.tsx
Normal file
115
screens/Event/AlertButtonStatusSection.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import AlertCustom from "@/components/Alert/AlertCustom";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Event_AlertButtonStatusSection({
|
||||
id,
|
||||
status,
|
||||
openAlert,
|
||||
setOpenAlert,
|
||||
openDeleteAlert,
|
||||
setOpenDeleteAlert,
|
||||
}: {
|
||||
id: string;
|
||||
status: string;
|
||||
openAlert: boolean;
|
||||
setOpenAlert: (value: boolean) => void;
|
||||
openDeleteAlert: boolean;
|
||||
setOpenDeleteAlert: (value: boolean) => void;
|
||||
}) {
|
||||
// --- Alert untuk aksi berdasarkan status ---
|
||||
const renderStatusAlert = () => {
|
||||
switch (status) {
|
||||
case "publish":
|
||||
return <></>;
|
||||
|
||||
case "review":
|
||||
return (
|
||||
<AlertCustom
|
||||
isVisible={openAlert}
|
||||
title="Batalkan Review"
|
||||
message="Apakah Anda yakin ingin membatalkan review?"
|
||||
textLeft="Batal"
|
||||
textRight="Ya"
|
||||
colorRight="green"
|
||||
onLeftPress={() => {
|
||||
setOpenAlert(false);
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
case "draft":
|
||||
return (
|
||||
<AlertCustom
|
||||
isVisible={openAlert}
|
||||
title="Ajukan Review ?"
|
||||
message="Apakah Anda yakin ingin mengajukan review kembali?"
|
||||
textLeft="Batal"
|
||||
textRight="Ya"
|
||||
colorRight="green"
|
||||
onLeftPress={() => {
|
||||
setOpenAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
case "reject":
|
||||
return (
|
||||
<AlertCustom
|
||||
isVisible={openAlert}
|
||||
title="Edit Kembali"
|
||||
message="Apakah Anda yakin ingin mengedit kembali event ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Ya"
|
||||
colorRight="green"
|
||||
onLeftPress={() => {
|
||||
setOpenAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Alert berdasarkan status */}
|
||||
{renderStatusAlert()}
|
||||
|
||||
{/* Alert untuk hapus - selalu muncul jika openDeleteAlert true */}
|
||||
<AlertCustom
|
||||
isVisible={openDeleteAlert}
|
||||
title="Hapus Event"
|
||||
message="Apakah Anda yakin ingin menghapus event ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Ya, Hapus"
|
||||
colorRight="red"
|
||||
onLeftPress={() => {
|
||||
setOpenDeleteAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
onRightPress={() => {
|
||||
// Aksi hapus event
|
||||
console.log("Menghapus event dengan ID:", id);
|
||||
setOpenDeleteAlert(false);
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
61
screens/Event/BoxDetailPublishSection.tsx
Normal file
61
screens/Event/BoxDetailPublishSection.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
|
||||
export default function Event_BoxDetailPublishSection({
|
||||
footerButton,
|
||||
}: {
|
||||
footerButton?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom bold align="center" size="xlarge">
|
||||
Judul event publish
|
||||
</TextCustom>
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.title}</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom>{item.value}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
{footerButton}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Lokasi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
{
|
||||
title: "Tipe Acara",
|
||||
value: "Workshop",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Mulai",
|
||||
value: "Senin, 18 Juli 2025, 10:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Berakhir",
|
||||
value: "Selasa, 19 Juli 2025, 12:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
];
|
||||
51
screens/Event/BoxPublishSection.tsx
Normal file
51
screens/Event/BoxPublishSection.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import { Href } from "expo-router";
|
||||
|
||||
export default function Event_BoxPublishSection({
|
||||
id,
|
||||
title,
|
||||
username,
|
||||
description,
|
||||
href,
|
||||
|
||||
// Avatar
|
||||
sourceAvatar,
|
||||
rightComponentAvatar,
|
||||
}: {
|
||||
id: string;
|
||||
title?: string;
|
||||
username?: string;
|
||||
description?: string;
|
||||
href: Href;
|
||||
|
||||
// Avatar
|
||||
sourceAvatar?: string;
|
||||
rightComponentAvatar?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BoxWithHeaderSection href={href}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${id}`}
|
||||
name={username || "Lorem ipsum dolor sit"}
|
||||
rightComponent={rightComponentAvatar}
|
||||
avatar={sourceAvatar as any}
|
||||
/>
|
||||
<TextCustom truncate bold>
|
||||
{title || "Lorem ipsum dolor sit"}
|
||||
</TextCustom>
|
||||
<TextCustom truncate={2}>
|
||||
{description ||
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa, obcaecati quia suscipit numquam, voluptates commodi porro impedit natus quos doloremque!"}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
76
screens/Event/ButtonStatusSection.tsx
Normal file
76
screens/Event/ButtonStatusSection.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { ButtonCustom, Grid } from "@/components";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Event_ButtonStatusSection({
|
||||
status,
|
||||
onOpenAlert,
|
||||
onOpenDeleteAlert,
|
||||
}: {
|
||||
status: string;
|
||||
onOpenAlert: (value: boolean) => void;
|
||||
onOpenDeleteAlert: (value: boolean) => void;
|
||||
}) {
|
||||
|
||||
const handleOpenAlert = () => {
|
||||
onOpenAlert(true);
|
||||
};
|
||||
|
||||
const handleOpenDeleteAlert = () => {
|
||||
onOpenDeleteAlert(true);
|
||||
};
|
||||
|
||||
const DeleteButton = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom backgroundColor="red" textColor="white" onPress={handleOpenDeleteAlert}>
|
||||
Hapus
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case "publish":
|
||||
return <></>;
|
||||
|
||||
case "review":
|
||||
return (
|
||||
<ButtonCustom onPress={handleOpenAlert}>
|
||||
Batalkan Review
|
||||
</ButtonCustom>
|
||||
);
|
||||
|
||||
case "draft":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleOpenAlert}>Ajukan Review</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
case "reject":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleOpenAlert}>Edit Kembali</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return <ButtonCustom disabled>Status Undifined</ButtonCustom>;
|
||||
}
|
||||
}
|
||||
17
screens/Event/menuDrawerDraft.tsx
Normal file
17
screens/Event/menuDrawerDraft.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export const menuDrawerDraftEvent = ({ id }: { id: string }) => [
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="create"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit event",
|
||||
path: `/(application)/(user)/event/${id}/edit`,
|
||||
},
|
||||
];
|
||||
17
screens/Event/menuDrawerPublish.tsx
Normal file
17
screens/Event/menuDrawerPublish.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { FontAwesome } from "@expo/vector-icons";
|
||||
|
||||
export const menuDrawerPublishEvent = ({ id }: { id: string }) => [
|
||||
{
|
||||
icon: (
|
||||
<FontAwesome
|
||||
name="users"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={AccentColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Daftar peserta",
|
||||
path: `/(application)/(user)/event/${id}/list-of-participants`,
|
||||
},
|
||||
];
|
||||
@@ -1,52 +1,55 @@
|
||||
import { TextCustom } from "@/components";
|
||||
import { ClickableCustom, TextCustom } from "@/components";
|
||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/FontAwesome";
|
||||
import { stylesHome } from "./homeViewStyle";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Home_BottomFeatureSection() {
|
||||
return (
|
||||
<>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10}/>
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
<ClickableCustom onPress={() => router.push("/job")}>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10} />
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ClickableCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,19 @@ export default function Home_FeatureSection() {
|
||||
<Ionicons name="analytics" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Event</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() =>
|
||||
router.push("/(application)/(user)/collaboration/(tabs)")
|
||||
}
|
||||
>
|
||||
<Ionicons name="share" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/(user)/voting/(tabs)")}
|
||||
>
|
||||
<Ionicons name="cube" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Voting</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
28
screens/Job/BoxDetailSection.tsx
Normal file
28
screens/Job/BoxDetailSection.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { BaseBox, StackCustom, DummyLandscapeImage, TextCustom } from "@/components";
|
||||
|
||||
export default function Job_BoxDetailSection({data}: {data: any}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom gap={"lg"}>
|
||||
<DummyLandscapeImage />
|
||||
|
||||
<TextCustom align="center" bold size="large">
|
||||
{data?.posisi}
|
||||
</TextCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Syarat & Ketentuan :</TextCustom>
|
||||
<TextCustom>{data?.syaratKetentuan}</TextCustom>
|
||||
</StackCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Deskripsi :</TextCustom>
|
||||
<TextCustom>{data?.deskripsi}</TextCustom>
|
||||
</StackCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
135
screens/Job/ButtonStatusSection.tsx
Normal file
135
screens/Job/ButtonStatusSection.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Job_ButtonStatusSection({
|
||||
status,
|
||||
}: {
|
||||
status: string;
|
||||
}) {
|
||||
const handleBatalkanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Batalkan Review",
|
||||
message: "Apakah Anda yakin ingin batalkan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleAjukanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Ajukan Review",
|
||||
message: "Apakah Anda yakin ingin ajukan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditKembali = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Edit Kembali",
|
||||
message: "Apakah Anda yakin ingin edit kembali ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenDeleteAlert = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Hapus",
|
||||
message: "Apakah Anda yakin ingin menghapus data ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Hapus",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const DeleteButton = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
backgroundColor="red"
|
||||
textColor="white"
|
||||
onPress={handleOpenDeleteAlert}
|
||||
>
|
||||
Hapus
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case "publish":
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Arsipkan");
|
||||
router.replace("/(application)/(user)/job/(tabs)/archive");
|
||||
}}
|
||||
>
|
||||
Arsipkan
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
|
||||
case "review":
|
||||
return (
|
||||
<ButtonCustom onPress={handleBatalkanReview}>
|
||||
Batalkan Review
|
||||
</ButtonCustom>
|
||||
);
|
||||
|
||||
case "draft":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleAjukanReview}>
|
||||
Ajukan Review
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
case "reject":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleEditKembali}>
|
||||
Edit Kembali
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return <ButtonCustom disabled>Status Undifined</ButtonCustom>;
|
||||
}
|
||||
}
|
||||
82
screens/Job/listDataDummy.ts
Normal file
82
screens/Job/listDataDummy.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
export const jobDataDummy = [
|
||||
{
|
||||
id: 1,
|
||||
posisi: "Front-End Developer",
|
||||
deskripsi:
|
||||
"Kami mencari Front-End Developer yang berpengalaman untuk membangun dan mengembangkan antarmuka pengguna web yang interaktif, responsif, dan menarik menggunakan React.js dan TypeScript. Kandidat akan bekerja sama dengan tim desain dan back-end untuk memastikan pengalaman pengguna yang optimal di berbagai perangkat.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Front-End Developer. Menguasai React.js, TypeScript, HTML, CSS (SCSS). Memahami prinsip-prinsip desain responsif dan pengalaman pengguna (UX). Pengalaman dengan Git dan CI/CD pipeline menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
posisi: "Back-End Developer",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab dalam membangun dan memelihara RESTful API, serta mengelola logika server dan interaksi dengan basis data. Anda akan bekerja dengan Node.js dan Express untuk mendukung berbagai kebutuhan aplikasi kami yang dinamis dan real-time.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman minimal 2 tahun sebagai Back-End Developer. Menguasai Node.js, Express.js, dan basis data (MongoDB, PostgreSQL). Paham prinsip REST API, middleware, dan keamanan data. Pengalaman dengan Docker dan testing (Jest/Mocha) menjadi nilai plus.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
posisi: "UI/UX Designer",
|
||||
deskripsi:
|
||||
"Kami mencari desainer kreatif untuk merancang pengalaman pengguna dan antarmuka yang modern dan intuitif. Anda akan terlibat dalam proses riset pengguna, wireframing, prototyping, dan pengujian desain untuk aplikasi web dan mobile.",
|
||||
syaratKetentuan:
|
||||
"Minimal 1 tahun pengalaman dalam UI/UX. Menguasai alat desain seperti Figma, Sketch, atau Adobe XD. Mampu membuat prototipe interaktif dan melakukan usability testing. Portofolio desain UI/UX yang pernah dikerjakan wajib dilampirkan.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
posisi: "DevOps Engineer",
|
||||
deskripsi:
|
||||
"Sebagai DevOps Engineer, Anda akan bertanggung jawab atas deployment otomatis, pemantauan sistem, dan infrastruktur cloud kami. Posisi ini akan fokus pada integrasi dan pengiriman berkelanjutan (CI/CD), skalabilitas, serta keamanan sistem.",
|
||||
syaratKetentuan:
|
||||
"Menguasai Docker, Kubernetes, Jenkins, GitLab CI, dan monitoring tools (Prometheus, Grafana). Memiliki pengalaman kerja dengan platform cloud seperti AWS atau GCP. Familiar dengan scripting Bash/Python dan prinsip keamanan DevOps.",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
posisi: "Mobile Developer",
|
||||
deskripsi:
|
||||
"Kami sedang mencari developer mobile yang terampil menggunakan Flutter untuk mengembangkan aplikasi lintas platform (iOS & Android) yang efisien dan user-friendly. Anda akan bekerja dalam tim produk untuk merancang dan mengimplementasikan fitur baru.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman mengembangkan aplikasi Flutter setidaknya 1 tahun. Memahami state management (Provider, BLoC, atau Riverpod). Pernah merilis aplikasi di Google Play Store atau App Store. Pengalaman dengan Firebase menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
posisi: "Data Analyst",
|
||||
deskripsi:
|
||||
"Sebagai Data Analyst, Anda akan mengumpulkan, menganalisis, dan menginterpretasikan data untuk mendukung keputusan bisnis. Anda akan membuat dashboard interaktif, laporan rutin, dan memberikan insight yang bisa ditindaklanjuti kepada tim manajemen.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman menggunakan SQL, Excel, dan tools visualisasi data seperti Power BI atau Tableau. Kemampuan analisis statistik dan storytelling data. Pengalaman dengan Python (pandas, numpy) merupakan keunggulan.",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
posisi: "Machine Learning Engineer",
|
||||
deskripsi:
|
||||
"Kami membuka posisi untuk insinyur Machine Learning yang akan mengembangkan dan mengimplementasikan model prediktif untuk mendukung berbagai kebutuhan bisnis. Anda akan bekerja dengan data dalam skala besar dan menerapkan teknik NLP, klasifikasi, dan rekomendasi.",
|
||||
syaratKetentuan:
|
||||
"Memiliki pengalaman minimal 1 tahun dalam ML project. Menguasai Python, scikit-learn, TensorFlow, atau PyTorch. Mampu memproses data besar dengan pandas, NumPy, dan Spark. Familiar dengan deployment model ML ke production.",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
posisi: "System Administrator",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab atas pengelolaan dan pemeliharaan sistem server Linux, jaringan internal, dan keamanan TI perusahaan. Termasuk setup server, monitoring, dan troubleshooting sistem operasional harian.",
|
||||
syaratKetentuan:
|
||||
"Berpengalaman mengelola server Linux (Ubuntu/CentOS), firewall, dan konfigurasi jaringan. Paham konsep load balancing, backup otomatis, dan disaster recovery. Kemampuan scripting (Bash, Python) menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
posisi: "Quality Assurance",
|
||||
deskripsi:
|
||||
"Kami mencari QA Engineer untuk memastikan kualitas produk melalui pengujian manual maupun otomatis. Anda akan menulis test case, menjalankan regresi test, dan bekerja erat dengan tim developer untuk menemukan serta memperbaiki bug lebih awal.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman dalam manual testing dan tools otomasi seperti Selenium, Cypress, atau Playwright. Memahami prinsip SDLC dan metodologi Agile. Mampu menulis dokumentasi QA dan membuat test report secara sistematis.",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
posisi: "Product Manager",
|
||||
deskripsi:
|
||||
"Sebagai Product Manager, Anda akan memimpin pengembangan produk mulai dari perencanaan, peluncuran, hingga peningkatan. Anda akan mengelola roadmap, prioritas fitur, dan bekerja lintas fungsi dengan tim desain, dev, dan bisnis.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Product Manager. Memahami Agile/Scrum. Keterampilan komunikasi dan analisis yang kuat. Pengalaman dalam tools seperti Jira, Confluence, dan alat wireframing. Kemampuan mengambil keputusan berbasis data.",
|
||||
},
|
||||
];
|
||||
@@ -6,7 +6,7 @@ import { router, useLocalSearchParams } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import AvatarAndBackground from "./AvatarAndBackground";
|
||||
|
||||
export default function ProfilSection() {
|
||||
export default function ProfileSection() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const listData = [
|
||||
55
screens/Voting/BoxPublishSection.tsx
Normal file
55
screens/Voting/BoxPublishSection.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
BoxWithHeaderSection,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
BadgeCustom,
|
||||
Grid,
|
||||
CircleContainer,
|
||||
} from "@/components";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default function Voting_BoxPublishSection({
|
||||
bottomComponent,
|
||||
}: {
|
||||
bottomComponent?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BoxWithHeaderSection>
|
||||
<AvatarUsernameAndOtherComponent avatarHref="/profile/1" />
|
||||
<Spacing />
|
||||
<StackCustom>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
Voting Title Here
|
||||
</TextCustom>
|
||||
|
||||
<BadgeCustom
|
||||
style={{ width: "70%", alignSelf: "center" }}
|
||||
variant="light"
|
||||
>
|
||||
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||
</BadgeCustom>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||
<CircleContainer value={"10"} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||
<CircleContainer value={"9"} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||
<CircleContainer value={"10"} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||
<CircleContainer value={"9"} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
{bottomComponent}
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -315,4 +315,9 @@ export const GStyles = StyleSheet.create({
|
||||
},
|
||||
|
||||
// =============== TEXT INPUT , TEXT AREA , SELECT =============== //
|
||||
|
||||
// =============== Alignment =============== //
|
||||
alignSelfCenter: {
|
||||
alignSelf: "center",
|
||||
},
|
||||
});
|
||||
|
||||
25
styles/tabs-styles.ts
Normal file
25
styles/tabs-styles.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { TabBarBackground } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { OS_IOS_HEIGHT, OS_ANDROID_HEIGHT } from "@/constants/constans-value";
|
||||
import { BottomTabNavigationOptions } from "@react-navigation/bottom-tabs";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export const TabsStyles: BottomTabNavigationOptions = {
|
||||
headerShown: false,
|
||||
tabBarActiveTintColor: MainColor.yellow,
|
||||
tabBarInactiveTintColor: MainColor.white_gray,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: OS_IOS_HEIGHT,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: OS_ANDROID_HEIGHT,
|
||||
},
|
||||
default: {},
|
||||
}),
|
||||
tabBarBackground: TabBarBackground,
|
||||
};
|
||||
Reference in New Issue
Block a user