Compare commits
9 Commits
event/22-j
...
collaborat
| Author | SHA1 | Date | |
|---|---|---|---|
| 603003865b | |||
| e02ae8e35d | |||
| 4f8ae2d7e0 | |||
| 360ac5807c | |||
| 8cb0054580 | |||
| 64d5a4308c | |||
| 30d61c84aa | |||
| 70e324e76e | |||
| 4474b46ff3 |
@@ -107,6 +107,48 @@ export default function UserLayout() {
|
|||||||
headerLeft: () => <BackButton />,
|
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 ========= */}
|
||||||
|
|
||||||
{/* ========== Forum Section ========= */}
|
{/* ========== Forum Section ========= */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -127,7 +169,7 @@ export default function UserLayout() {
|
|||||||
name="forum/[id]/forumku"
|
name="forum/[id]/forumku"
|
||||||
options={{
|
options={{
|
||||||
title: "Forumku",
|
title: "Forumku",
|
||||||
headerLeft: () => <BackButton icon={'close'} />,
|
headerLeft: () => <BackButton icon={"close"} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
37
app/(application)/(user)/collaboration/(tabs)/_layout.tsx
Normal file
37
app/(application)/(user)/collaboration/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
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 }) => (
|
||||||
|
<Ionicons size={20} name="home" 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,31 +1,11 @@
|
|||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { OS_IOS_HEIGHT, OS_ANDROID_HEIGHT } from "@/constants/constans-value";
|
|
||||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs } from "expo-router";
|
||||||
import { Platform, View } from "react-native";
|
|
||||||
|
|
||||||
export default function EventTabsLayout() {
|
export default function EventTabsLayout() {
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={TabsStyles}
|
||||||
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.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
@@ -67,15 +47,3 @@ export default function EventTabsLayout() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomTabBarBackground() {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: MainColor.darkblue,
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderTopColor: AccentColor.blue,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
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,
|
ScrollView,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
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");
|
const { width } = Dimensions.get("window");
|
||||||
|
|
||||||
@@ -16,7 +18,9 @@ const { width } = Dimensions.get("window");
|
|||||||
const HomeScreen = () => (
|
const HomeScreen = () => (
|
||||||
<View style={styles.screen}>
|
<View style={styles.screen}>
|
||||||
<Text style={styles.screenTitle}>Selamat Datang!</Text>
|
<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>
|
</View>
|
||||||
);
|
);
|
||||||
const SearchScreen = () => (
|
const SearchScreen = () => (
|
||||||
@@ -65,9 +69,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: any) => (
|
|||||||
|
|
||||||
// Main Custom Tab Navigator
|
// Main Custom Tab Navigator
|
||||||
const CustomTabNavigator = () => {
|
const CustomTabNavigator = () => {
|
||||||
const [activeTab, setActiveTab] = React.useState(
|
const [activeTab, setActiveTab] = React.useState("home");
|
||||||
'home'
|
|
||||||
);
|
|
||||||
const [showHome, setShowHome] = React.useState(true);
|
const [showHome, setShowHome] = React.useState(true);
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
@@ -97,13 +99,12 @@ const CustomTabNavigator = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// Function untuk handle tab press
|
// Function untuk handle tab press
|
||||||
const handleTabPress = (tabId: string) => {
|
const handleTabPress = (tabId: string) => {
|
||||||
setActiveTab(tabId);
|
setActiveTab(tabId);
|
||||||
setShowHome(false); // Hide home when any tab is pressed
|
setShowHome(false); // Hide home when any tab is pressed
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine which component to show
|
// Determine which component to show
|
||||||
const getActiveComponent = () => {
|
const getActiveComponent = () => {
|
||||||
if (showHome || activeTab === "home") {
|
if (showHome || activeTab === "home") {
|
||||||
@@ -111,38 +112,46 @@ const CustomTabNavigator = () => {
|
|||||||
}
|
}
|
||||||
// const selectedTab = tabs.find((tab) => tab.id === activeTab);
|
// const selectedTab = tabs.find((tab) => tab.id === activeTab);
|
||||||
// return selectedTab ? selectedTab.component : HomeScreen;
|
// return selectedTab ? selectedTab.component : HomeScreen;
|
||||||
return HomeScreen
|
return HomeScreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActiveComponent = getActiveComponent();
|
const ActiveComponent = getActiveComponent();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<>
|
||||||
{/* Content Area */}
|
<Stack.Screen
|
||||||
<ScrollView>
|
options={{
|
||||||
<View style={styles.content}>
|
title: "Custom Tab Navigator",
|
||||||
<ActiveComponent />
|
}}
|
||||||
</View>
|
/>
|
||||||
</ScrollView>
|
<EventDetailScreen />
|
||||||
|
</>
|
||||||
|
// <View style={styles.container}>
|
||||||
|
// {/* Content Area */}
|
||||||
|
// <ScrollView>
|
||||||
|
// <View style={styles.content}>
|
||||||
|
// <ActiveComponent />
|
||||||
|
// </View>
|
||||||
|
// </ScrollView>
|
||||||
|
|
||||||
{/* Custom Tab Bar */}
|
// {/* Custom Tab Bar */}
|
||||||
<View style={styles.tabBar}>
|
// <View style={styles.tabBar}>
|
||||||
<View style={styles.tabContainer}>
|
// <View style={styles.tabContainer}>
|
||||||
{tabs.map((e) => (
|
// {tabs.map((e) => (
|
||||||
<CustomTab
|
// <CustomTab
|
||||||
key={e.id}
|
// key={e.id}
|
||||||
icon={activeTab === e.id ? e.activeIcon : e.icon}
|
// icon={activeTab === e.id ? e.activeIcon : e.icon}
|
||||||
label={e.label}
|
// label={e.label}
|
||||||
isActive={activeTab === e.id && !showHome}
|
// isActive={activeTab === e.id && !showHome}
|
||||||
onPress={() => {
|
// onPress={() => {
|
||||||
handleTabPress(e.id);
|
// handleTabPress(e.id);
|
||||||
router.push(e.path as any);
|
// router.push(e.path as any);
|
||||||
}}
|
// }}
|
||||||
/>
|
// />
|
||||||
))}
|
// ))}
|
||||||
</View>
|
// </View>
|
||||||
</View>
|
// </View>
|
||||||
</View>
|
// </View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
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?.(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ interface BaseBoxProps {
|
|||||||
paddingBottom?: number;
|
paddingBottom?: number;
|
||||||
paddingInline?: number;
|
paddingInline?: number;
|
||||||
paddingBlock?: number;
|
paddingBlock?: number;
|
||||||
|
backgroundColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BaseBox({
|
export default function BaseBox({
|
||||||
@@ -34,6 +35,7 @@ export default function BaseBox({
|
|||||||
paddingInline = PADDING_SMALL,
|
paddingInline = PADDING_SMALL,
|
||||||
paddingTop = PADDING_MEDIUM,
|
paddingTop = PADDING_MEDIUM,
|
||||||
paddingBottom = PADDING_MEDIUM,
|
paddingBottom = PADDING_MEDIUM,
|
||||||
|
backgroundColor = AccentColor.darkblue,
|
||||||
}: BaseBoxProps) {
|
}: BaseBoxProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -44,7 +46,7 @@ export default function BaseBox({
|
|||||||
onPress={href ? () => router.navigate(href) : onPress}
|
onPress={href ? () => router.navigate(href) : onPress}
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
backgroundColor: AccentColor.darkblue,
|
backgroundColor,
|
||||||
borderColor: AccentColor.blue,
|
borderColor: AccentColor.blue,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
@@ -63,7 +65,7 @@ export default function BaseBox({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
backgroundColor: AccentColor.darkblue,
|
backgroundColor,
|
||||||
borderColor: AccentColor.blue,
|
borderColor: AccentColor.blue,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default function BoxWithHeaderSection({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseBox href={href} onPress={onPress} paddingTop={5}>
|
<BaseBox href={href} onPress={onPress} style={{ paddingTop: 5 }}>
|
||||||
{children}
|
{children}
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
</>
|
</>
|
||||||
|
|||||||
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
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 { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
import { DRAWER_HEIGHT } from "@/constants/constans-value";
|
import { DRAWER_HEIGHT } from "@/constants/constans-value";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
interface DrawerCustomProps {
|
interface DrawerCustomProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
height?: number;
|
height?: number | "auto";
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
drawerAnim?: Animated.Value;
|
drawerAnim?: Animated.Value;
|
||||||
closeDrawer: () => void;
|
closeDrawer: () => void;
|
||||||
// openLogoutAlert: () => void;
|
// openLogoutAlert: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param drawerAnim
|
* @param drawerAnim
|
||||||
* @example const drawerAnim = useRef(new Animated.Value(DRAWER_HEIGHT)).current; // mulai di luar bawah layar
|
* @example const drawerAnim = useRef(new Animated.Value(DRAWER_HEIGHT)).current; // mulai di luar bawah layar
|
||||||
*/
|
*/
|
||||||
export default function DrawerCustom({
|
export default function DrawerCustom({
|
||||||
@@ -34,7 +34,9 @@ export default function DrawerCustom({
|
|||||||
}: // openLogoutAlert,
|
}: // openLogoutAlert,
|
||||||
DrawerCustomProps) {
|
DrawerCustomProps) {
|
||||||
const drawerAnima = useRef(
|
const drawerAnima = useRef(
|
||||||
new Animated.Value(height || DRAWER_HEIGHT)
|
new Animated.Value(
|
||||||
|
height === "auto" ? DRAWER_HEIGHT : height || DRAWER_HEIGHT
|
||||||
|
)
|
||||||
).current;
|
).current;
|
||||||
// Efek untuk handle open/close drawer
|
// Efek untuk handle open/close drawer
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -46,7 +48,7 @@ DrawerCustomProps) {
|
|||||||
}).start();
|
}).start();
|
||||||
} else {
|
} else {
|
||||||
Animated.timing(drawerAnima, {
|
Animated.timing(drawerAnima, {
|
||||||
toValue: height || DRAWER_HEIGHT,
|
toValue: height === "auto" ? DRAWER_HEIGHT : height || DRAWER_HEIGHT,
|
||||||
duration: 300,
|
duration: 300,
|
||||||
useNativeDriver: true,
|
useNativeDriver: true,
|
||||||
}).start();
|
}).start();
|
||||||
@@ -99,7 +101,7 @@ DrawerCustomProps) {
|
|||||||
style={[
|
style={[
|
||||||
styles.drawer,
|
styles.drawer,
|
||||||
{
|
{
|
||||||
height: height || DRAWER_HEIGHT,
|
height: height === "auto" ? "auto" : height || DRAWER_HEIGHT,
|
||||||
transform: [{ translateY: drawerAnima }],
|
transform: [{ translateY: drawerAnima }],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@@ -110,34 +112,7 @@ DrawerCustomProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
{height === "auto" && <SafeAreaView edges={["bottom"]} />}
|
||||||
{/* <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> */}
|
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,13 +2,26 @@ import { AccentColor, MainColor } from "@/constants/color-palet";
|
|||||||
import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||||
import { IMenuDrawerItem } from "../_Interface/types";
|
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;
|
const numColumns = columns;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{data.map((item: IMenuDrawerItem, index: any) => (
|
{data.map((item: IMenuDrawerItemProps, index: any) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={index}
|
key={index}
|
||||||
style={[styles.itemContainer, { flexBasis: `${100 / numColumns}%` }]}
|
style={[styles.itemContainer, { flexBasis: `${100 / numColumns}%` }]}
|
||||||
|
|||||||
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" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
components/_Icon/index.ts
Normal file
3
components/_Icon/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import IconEdit from "./IconEdit";
|
||||||
|
|
||||||
|
export { IconEdit };
|
||||||
@@ -2,21 +2,23 @@ import { ImageSourcePropType, View } from "react-native";
|
|||||||
import Grid from "../Grid/GridCustom";
|
import Grid from "../Grid/GridCustom";
|
||||||
import AvatarCustom from "../Image/AvatarCustom";
|
import AvatarCustom from "../Image/AvatarCustom";
|
||||||
import TextCustom from "../Text/TextCustom";
|
import TextCustom from "../Text/TextCustom";
|
||||||
|
import Divider from "../Divider/Divider"
|
||||||
|
|
||||||
const AvatarUsernameAndOtherComponent = ({
|
const AvatarUsernameAndOtherComponent = ({
|
||||||
avatarHref,
|
avatarHref,
|
||||||
avatar,
|
avatar,
|
||||||
name,
|
name,
|
||||||
rightComponent,
|
rightComponent,
|
||||||
|
withBottomLine = false,
|
||||||
}: {
|
}: {
|
||||||
avatarHref?: string;
|
avatarHref?: string;
|
||||||
avatar?: ImageSourcePropType;
|
avatar?: ImageSourcePropType;
|
||||||
name?: string;
|
name?: string;
|
||||||
rightComponent?: React.ReactNode;
|
rightComponent?: React.ReactNode;
|
||||||
|
withBottomLine?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View>
|
|
||||||
<Grid containerStyle={{ zIndex: 10 }}>
|
<Grid containerStyle={{ zIndex: 10 }}>
|
||||||
<Grid.Col span={2}>
|
<Grid.Col span={2}>
|
||||||
<AvatarCustom source={avatar} href={avatarHref as any} />
|
<AvatarCustom source={avatar} href={avatarHref as any} />
|
||||||
@@ -38,6 +40,8 @@ const AvatarUsernameAndOtherComponent = ({
|
|||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{withBottomLine && <Divider marginTop={0} />}
|
||||||
|
<View>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
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,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
// Alert
|
// Alert
|
||||||
import AlertCustom from "./Alert/AlertCustom";
|
import AlertCustom from "./Alert/AlertCustom";
|
||||||
|
import AlertDefaultSystem from "./Alert/AlertDefaultSystem";
|
||||||
// Button
|
// Button
|
||||||
import LeftButtonCustom from "./Button/BackButton";
|
import LeftButtonCustom from "./Button/BackButton";
|
||||||
import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
||||||
import ButtonCustom from "./Button/ButtonCustom";
|
import ButtonCustom from "./Button/ButtonCustom";
|
||||||
import DotButton from "./Button/DotButton";
|
import DotButton from "./Button/DotButton";
|
||||||
|
import FloatingButton from "./Button/FloatingButton";
|
||||||
|
// Checkbox
|
||||||
|
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
||||||
|
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
||||||
// Drawer
|
// Drawer
|
||||||
import DrawerCustom from "./Drawer/DrawerCustom";
|
import DrawerCustom from "./Drawer/DrawerCustom";
|
||||||
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
|
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
|
||||||
@@ -29,6 +34,7 @@ import SelectCustom from "./Select/SelectCustom";
|
|||||||
import AvatarCustom from "./Image/AvatarCustom";
|
import AvatarCustom from "./Image/AvatarCustom";
|
||||||
import LandscapeFrameUploaded from "./Image/LandscapeFrameUploaded";
|
import LandscapeFrameUploaded from "./Image/LandscapeFrameUploaded";
|
||||||
// Divider
|
// Divider
|
||||||
|
import Divider from "./Divider/Divider";
|
||||||
import DividerCustom from "./Divider/DividerCustom";
|
import DividerCustom from "./Divider/DividerCustom";
|
||||||
// Map
|
// Map
|
||||||
import MapCustom from "./Map/MapCustom";
|
import MapCustom from "./Map/MapCustom";
|
||||||
@@ -41,31 +47,45 @@ import ScrollableCustom from "./Scroll/ScrollCustom";
|
|||||||
// ShareComponent
|
// ShareComponent
|
||||||
import AvatarUsernameAndOtherComponent from "./_ShareComponent/AvataraAndOtherHeaderComponent";
|
import AvatarUsernameAndOtherComponent from "./_ShareComponent/AvataraAndOtherHeaderComponent";
|
||||||
import Spacing from "./_ShareComponent/Spacing";
|
import Spacing from "./_ShareComponent/Spacing";
|
||||||
|
import TabBarBackground from "./_ShareComponent/TabBarBackground";
|
||||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AlertCustom,
|
AlertCustom,
|
||||||
|
AlertDefaultSystem,
|
||||||
// Image
|
// Image
|
||||||
AvatarCustom,
|
AvatarCustom,
|
||||||
// ShareComponent
|
// ShareComponent
|
||||||
AvatarUsernameAndOtherComponent, LeftButtonCustom as BackButton,
|
AvatarUsernameAndOtherComponent,
|
||||||
|
// Button
|
||||||
|
LeftButtonCustom as BackButton,
|
||||||
// Box
|
// Box
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter, BoxWithHeaderSection,
|
BoxButtonOnFooter,
|
||||||
// Button
|
BoxWithHeaderSection,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
|
DotButton,
|
||||||
// Center
|
// Center
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
|
// Checkbox
|
||||||
|
CheckboxCustom,
|
||||||
|
CheckboxGroup,
|
||||||
// Clickable
|
// Clickable
|
||||||
ClickableCustom,
|
ClickableCustom,
|
||||||
// Divider
|
// Divider
|
||||||
DividerCustom, DotButton,
|
Divider,
|
||||||
|
DividerCustom,
|
||||||
// Drawer
|
// Drawer
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
|
FloatingButton,
|
||||||
// Grid
|
// Grid
|
||||||
Grid, InformationBox, LandscapeFrameUploaded,
|
Grid,
|
||||||
|
InformationBox,
|
||||||
|
LandscapeFrameUploaded,
|
||||||
// Map
|
// Map
|
||||||
MapCustom, MenuDrawerDynamicGrid,
|
MapCustom,
|
||||||
|
MenuDrawerDynamicGrid,
|
||||||
// Scroll
|
// Scroll
|
||||||
ScrollableCustom,
|
ScrollableCustom,
|
||||||
// Select
|
// Select
|
||||||
@@ -74,6 +94,7 @@ export {
|
|||||||
Spacing,
|
Spacing,
|
||||||
// Stack
|
// Stack
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TabBarBackground,
|
||||||
// TextArea
|
// TextArea
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
// Text
|
// Text
|
||||||
@@ -81,6 +102,5 @@ export {
|
|||||||
// TextInput
|
// TextInput
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
// ViewWrapper
|
// ViewWrapper
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,13 +30,14 @@ export default function LoginView() {
|
|||||||
const id = randomAlfabet + randomNumber + fixNumber;
|
const id = randomAlfabet + randomNumber + fixNumber;
|
||||||
console.log("login user id :", id);
|
console.log("login user id :", id);
|
||||||
|
|
||||||
router.navigate("/verification");
|
// router.navigate("/verification");
|
||||||
// router.navigate(`/(application)/(user)/profile/${id}`);
|
// 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)/profile/${id}/edit`);
|
||||||
// router.navigate(`/(application)/(user)/portofolio/${id}`)
|
// router.navigate(`/(application)/(user)/portofolio/${id}`)
|
||||||
// router.navigate(`/(application)/(image)/preview-image/${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 (
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -14,7 +14,10 @@ export default function Home_FeatureSection() {
|
|||||||
<Ionicons name="analytics" size={48} color="white" />
|
<Ionicons name="analytics" size={48} color="white" />
|
||||||
<Text style={stylesHome.gridLabel}>Event</Text>
|
<Text style={stylesHome.gridLabel}>Event</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={stylesHome.gridItem}>
|
<TouchableOpacity
|
||||||
|
style={stylesHome.gridItem}
|
||||||
|
onPress={() => router.push("/(application)/(user)/collaboration/(tabs)")}
|
||||||
|
>
|
||||||
<Ionicons name="share" size={48} color="white" />
|
<Ionicons name="share" size={48} color="white" />
|
||||||
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
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