Compare commits
26 Commits
resourcing
...
collaborat
| Author | SHA1 | Date | |
|---|---|---|---|
| 603003865b | |||
| e02ae8e35d | |||
| 4f8ae2d7e0 | |||
| 360ac5807c | |||
| 8cb0054580 | |||
| 64d5a4308c | |||
| 30d61c84aa | |||
| 70e324e76e | |||
| 4474b46ff3 | |||
| aa4ea9fb0c | |||
| 81d86885f4 | |||
| 814f261881 | |||
| 7889d25a44 | |||
| ce0e82e627 | |||
| 08dfd62bfd | |||
| c8cc0f0232 | |||
| b844a8151d | |||
| f9e96aa077 | |||
| b8b1efc71e | |||
| eaf0ebfb0a | |||
| e68d366d49 | |||
| 9999f78ed4 | |||
| 2be5afe5ca | |||
| 24913a9f97 | |||
| 3376336c55 | |||
| a0dad5618a |
@@ -76,6 +76,13 @@ export default function UserLayout() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="event/create"
|
||||||
|
options={{
|
||||||
|
title: "Tambah Event",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="event/detail/[id]"
|
name="event/detail/[id]"
|
||||||
@@ -85,6 +92,64 @@ 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 ========= */}
|
||||||
|
|
||||||
{/* ========== Forum Section ========= */}
|
{/* ========== Forum Section ========= */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="forum/create"
|
name="forum/create"
|
||||||
@@ -104,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
|
||||||
@@ -114,6 +179,34 @@ export default function UserLayout() {
|
|||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/report-commentar"
|
||||||
|
options={{
|
||||||
|
title: "Laporkan Komentar",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/other-report-commentar"
|
||||||
|
options={{
|
||||||
|
title: "Laporkan Komentar",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/report-posting"
|
||||||
|
options={{
|
||||||
|
title: "Laporkan Diskusi",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/other-report-posting"
|
||||||
|
options={{
|
||||||
|
title: "Laporkan Diskusi",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* ========== Maps Section ========= */}
|
{/* ========== Maps Section ========= */}
|
||||||
<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,32 +1,16 @@
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs } from "expo-router";
|
||||||
|
|
||||||
export default function EventLayout() {
|
export default function EventTabsLayout() {
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={TabsStyles}
|
||||||
headerShown: false,
|
|
||||||
tabBarActiveTintColor: MainColor.yellow,
|
|
||||||
tabBarInactiveTintColor: MainColor.white_gray,
|
|
||||||
tabBarStyle: {
|
|
||||||
backgroundColor: MainColor.darkblue,
|
|
||||||
},
|
|
||||||
// tabBarButton: HapticTab,
|
|
||||||
// tabBarBackground: BlurTabBarBackground,
|
|
||||||
// tabBarStyle: Platform.select({
|
|
||||||
// ios: {
|
|
||||||
// // Use a transparent background on iOS to show the blur effect
|
|
||||||
// position: "absolute",
|
|
||||||
// },
|
|
||||||
// default: {},
|
|
||||||
// }),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
options={{
|
options={{
|
||||||
title: "Home",
|
title: "Beranda",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => (
|
||||||
<Ionicons size={20} name="home" color={color} />
|
<Ionicons size={20} name="home" color={color} />
|
||||||
),
|
),
|
||||||
@@ -42,7 +26,7 @@ export default function EventLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="kontribusi"
|
name="contribution"
|
||||||
options={{
|
options={{
|
||||||
title: "Kontribusi",
|
title: "Kontribusi",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => (
|
||||||
@@ -51,7 +35,7 @@ export default function EventLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="riwayat"
|
name="history"
|
||||||
options={{
|
options={{
|
||||||
title: "Riwayat",
|
title: "Riwayat",
|
||||||
tabBarIcon: ({ color }) => (
|
tabBarIcon: ({ color }) => (
|
||||||
@@ -62,3 +46,4 @@ export default function EventLayout() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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,25 +1,23 @@
|
|||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import FloatingButton from "@/components/Button/FloatingButton";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { Text, TouchableHighlight, View } from "react-native";
|
|
||||||
|
|
||||||
export default function Event() {
|
export default function EventBeranda() {
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper
|
||||||
<TouchableHighlight onPress={() => router.push("/event/detail/1")}>
|
hideFooter
|
||||||
<View
|
floatingButton={
|
||||||
style={{
|
<FloatingButton onPress={() => router.push("/event/create")} />
|
||||||
padding: 20,
|
}
|
||||||
backgroundColor: MainColor.darkblue,
|
>
|
||||||
borderRadius: 10,
|
{Array.from({ length: 10 }).map((_, index) => (
|
||||||
borderColor: AccentColor.blue,
|
<Event_BoxPublishSection
|
||||||
borderWidth: 1,
|
key={index}
|
||||||
}}
|
id={index.toString()}
|
||||||
>
|
href={`/event/${index}/publish`}
|
||||||
<Text style={GStyles.textLabel}>Event</Text>
|
/>
|
||||||
</View>
|
))}
|
||||||
</TouchableHighlight>
|
|
||||||
</ViewWrapper>
|
</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 ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||||
import { Text } from "react-native";
|
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 (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper headerComponent={scrollComponent}>
|
||||||
<Text style={GStyles.textLabel}>Status</Text>
|
<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>
|
</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 })}
|
||||||
|
columns={4}
|
||||||
|
onPressItem={handlePress}
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
109
app/(application)/(user)/event/create.tsx
Normal file
109
app/(application)/(user)/event/create.tsx
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
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 EventCreate() {
|
||||||
|
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 disimpan");
|
||||||
|
router.navigate("/event/status");
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonSubmit = (
|
||||||
|
<ButtonCustom title="Simpan" onPress={handlerSubmit} />
|
||||||
|
// <BoxButtonOnFooter>
|
||||||
|
// </BoxButtonOnFooter>
|
||||||
|
);
|
||||||
|
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||||
import { listDataDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
|
||||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -47,12 +47,14 @@ export default function Forumku() {
|
|||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
{listDataDummyCommentarForum.map((e, i) => (
|
{listDummyDiscussionForum.map((e, i) => (
|
||||||
<Forum_BoxDetailSection
|
<Forum_BoxDetailSection
|
||||||
key={i}
|
key={i}
|
||||||
data={e}
|
data={e}
|
||||||
setOpenDrawer={setOpenDrawer}
|
setOpenDrawer={setOpenDrawer}
|
||||||
setStatus={setStatus}
|
setStatus={setStatus}
|
||||||
|
isTruncate={true}
|
||||||
|
href={`/forum/${id}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
@@ -12,9 +11,9 @@ import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
|
|||||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||||
import { listDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
import { listDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
||||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||||
|
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function ForumDetail() {
|
export default function ForumDetail() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
@@ -27,7 +26,7 @@ export default function ForumDetail() {
|
|||||||
|
|
||||||
// Comentar
|
// Comentar
|
||||||
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
|
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
|
||||||
const [statusCommentar, setStatusCommentar] = useState("");
|
const [alertDeleteCommentar, setAlertDeleteCommentar] = useState(false);
|
||||||
|
|
||||||
const dataDummy = {
|
const dataDummy = {
|
||||||
name: "Bagas",
|
name: "Bagas",
|
||||||
@@ -78,7 +77,6 @@ export default function ForumDetail() {
|
|||||||
key={i}
|
key={i}
|
||||||
data={e}
|
data={e}
|
||||||
setOpenDrawer={setOpenDrawerCommentar}
|
setOpenDrawer={setOpenDrawerCommentar}
|
||||||
setStatus={setStatusCommentar}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
@@ -145,16 +143,34 @@ export default function ForumDetail() {
|
|||||||
isVisible={openDrawerCommentar}
|
isVisible={openDrawerCommentar}
|
||||||
closeDrawer={() => setOpenDrawerCommentar(false)}
|
closeDrawer={() => setOpenDrawerCommentar(false)}
|
||||||
>
|
>
|
||||||
<Forum_MenuDrawerBerandaSection
|
<Forum_MenuDrawerCommentar
|
||||||
id={id as string}
|
id={id as string}
|
||||||
status={statusCommentar}
|
|
||||||
setIsDrawerOpen={() => {
|
setIsDrawerOpen={() => {
|
||||||
setOpenDrawerCommentar(false);
|
setOpenDrawerCommentar(false);
|
||||||
}}
|
}}
|
||||||
setShowDeleteAlert={setDeleteAlert}
|
setShowDeleteAlert={setAlertDeleteCommentar}
|
||||||
setShowAlertStatus={setAlertStatus}
|
|
||||||
/>
|
/>
|
||||||
</DrawerCustom>
|
</DrawerCustom>
|
||||||
|
|
||||||
|
{/* Alert Delete Commentar */}
|
||||||
|
<AlertCustom
|
||||||
|
isVisible={alertDeleteCommentar}
|
||||||
|
title="Hapus Komentar"
|
||||||
|
message="Apakah Anda yakin ingin menghapus komentar ini?"
|
||||||
|
onLeftPress={() => {
|
||||||
|
setOpenDrawerCommentar(false);
|
||||||
|
setAlertDeleteCommentar(false);
|
||||||
|
console.log("Batal");
|
||||||
|
}}
|
||||||
|
onRightPress={() => {
|
||||||
|
setOpenDrawerCommentar(false);
|
||||||
|
setAlertDeleteCommentar(false);
|
||||||
|
console.log("Hapus commentar");
|
||||||
|
}}
|
||||||
|
textLeft="Batal"
|
||||||
|
textRight="Hapus"
|
||||||
|
colorRight={MainColor.red}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
ButtonCustom,
|
||||||
|
TextAreaCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function ForumOtherReportCommentar() {
|
||||||
|
const handleSubmit = (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={MainColor.red}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Report lainnya");
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Report
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper footerComponent={handleSubmit}>
|
||||||
|
<TextAreaCustom placeholder="Laporkan Komentar" />
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
32
app/(application)/(user)/forum/[id]/other-report-posting.tsx
Normal file
32
app/(application)/(user)/forum/[id]/other-report-posting.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
ButtonCustom,
|
||||||
|
TextAreaCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function ForumOtherReportPosting() {
|
||||||
|
const handleSubmit = (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={MainColor.red}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Report lainnya");
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Report
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper footerComponent={handleSubmit}>
|
||||||
|
<TextAreaCustom placeholder="Laporkan Diskusi" />
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
app/(application)/(user)/forum/[id]/report-commentar.tsx
Normal file
42
app/(application)/(user)/forum/[id]/report-commentar.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
ViewWrapper
|
||||||
|
} from "@/components";
|
||||||
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
|
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function ForumReportCommentar() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<Forum_ReportListSection />
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={MainColor.red}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Report");
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Report
|
||||||
|
</ButtonCustom>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={AccentColor.blue}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Lainnya");
|
||||||
|
router.replace("/forum/[id]/other-report-commentar");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Lainnya
|
||||||
|
</ButtonCustom>
|
||||||
|
<Spacing/>
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
37
app/(application)/(user)/forum/[id]/report-posting.tsx
Normal file
37
app/(application)/(user)/forum/[id]/report-posting.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { ViewWrapper, StackCustom, ButtonCustom, Spacing } from "@/components";
|
||||||
|
import { MainColor, AccentColor } from "@/constants/color-palet";
|
||||||
|
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function ForumReportPosting() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<Forum_ReportListSection />
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={MainColor.red}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Report");
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Report
|
||||||
|
</ButtonCustom>
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor={AccentColor.blue}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Lainnya");
|
||||||
|
router.replace("/forum/[id]/other-report-posting");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Lainnya
|
||||||
|
</ButtonCustom>
|
||||||
|
<Spacing />
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,18 +11,18 @@ import { useState } from "react";
|
|||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{ id: 1, label: "Semua" },
|
{ value: "all", label: "Semua" },
|
||||||
{ id: 2, label: "Event" },
|
{ value: "event", label: "Event" },
|
||||||
{ id: 3, label: "Job" },
|
{ value: "job", label: "Job" },
|
||||||
{ id: 4, label: "Voting" },
|
{ value: "voting", label: "Voting" },
|
||||||
{ id: 5, label: "Donasi" },
|
{ value: "donasi", label: "Donasi" },
|
||||||
{ id: 6, label: "Investasi" },
|
{ value: "investasi", label: "Investasi" },
|
||||||
{ id: 7, label: "Forum" },
|
{ value: "forum", label: "Forum" },
|
||||||
{ id: 8, label: "Collaboration" },
|
{ value: "collaboration", label: "Collaboration" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const selectedCategory = (id: number) => {
|
const selectedCategory = (value: string) => {
|
||||||
const category = categories.find((c) => c.id === id);
|
const category = categories.find((c) => c.value === value);
|
||||||
return category?.label;
|
return category?.label;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ const BoxNotification = ({
|
|||||||
activeCategory,
|
activeCategory,
|
||||||
}: {
|
}: {
|
||||||
index: number;
|
index: number;
|
||||||
activeCategory: number | null;
|
activeCategory: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -39,13 +39,13 @@ const BoxNotification = ({
|
|||||||
onPress={() =>
|
onPress={() =>
|
||||||
console.log(
|
console.log(
|
||||||
"Notification >",
|
"Notification >",
|
||||||
selectedCategory(activeCategory as number)
|
selectedCategory(activeCategory as string)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom bold>
|
<TextCustom bold>
|
||||||
# {selectedCategory(activeCategory as number)}
|
# {selectedCategory(activeCategory as string)}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -81,38 +81,31 @@ const BoxNotification = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const [activeCategory, setActiveCategory] = useState<number | null>(1);
|
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
const handlePress = (item: any) => {
|
||||||
setActiveCategory(item.id);
|
setActiveCategory(item.value);
|
||||||
// tambahkan logika lain seperti filter dsb.
|
// tambahkan logika lain seperti filter dsb.
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
headerComponent={
|
headerComponent={
|
||||||
<ScrollableCustom
|
<ScrollableCustom
|
||||||
data={categories}
|
data={categories.map((e, i) => ({
|
||||||
|
id: i,
|
||||||
|
label: e.label,
|
||||||
|
value: e.value,
|
||||||
|
}))}
|
||||||
onButtonPress={handlePress}
|
onButtonPress={handlePress}
|
||||||
activeId={activeCategory as any}
|
activeId={activeCategory as string}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 20 }).map((e, i) => (
|
{Array.from({ length: 20 }).map((e, i) => (
|
||||||
<View key={i}>
|
<View key={i}>
|
||||||
<BoxNotification index={i} activeCategory={activeCategory} />
|
<BoxNotification index={i} activeCategory={activeCategory as any} />
|
||||||
</View>
|
</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>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DrawerCustom } from "@/components";
|
import { AlertCustom, DrawerCustom } from "@/components";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
@@ -7,13 +7,14 @@ import Portofolio_MenuDrawerSection from "@/screens/Portofolio/MenuDrawer";
|
|||||||
import PorfofolioSection from "@/screens/Portofolio/PorfofolioSection";
|
import PorfofolioSection from "@/screens/Portofolio/PorfofolioSection";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Stack, useLocalSearchParams } from "expo-router";
|
import { Stack, useLocalSearchParams, router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { TouchableOpacity } from "react-native";
|
import { TouchableOpacity } from "react-native";
|
||||||
|
|
||||||
export default function Portofolio() {
|
export default function Portofolio() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||||
|
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||||
|
|
||||||
const openDrawer = () => {
|
const openDrawer = () => {
|
||||||
setIsDrawerOpen(true);
|
setIsDrawerOpen(true);
|
||||||
@@ -42,7 +43,7 @@ export default function Portofolio() {
|
|||||||
headerTitleStyle: GStyles.headerTitleStyle,
|
headerTitleStyle: GStyles.headerTitleStyle,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<PorfofolioSection />
|
<PorfofolioSection setShowDeleteAlert={setDeleteAlert} />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
{/* Drawer Komponen Eksternal */}
|
{/* Drawer Komponen Eksternal */}
|
||||||
@@ -56,6 +57,22 @@ export default function Portofolio() {
|
|||||||
setIsDrawerOpen={setIsDrawerOpen}
|
setIsDrawerOpen={setIsDrawerOpen}
|
||||||
/>
|
/>
|
||||||
</DrawerCustom>
|
</DrawerCustom>
|
||||||
|
|
||||||
|
{/* Alert Delete */}
|
||||||
|
<AlertCustom
|
||||||
|
isVisible={deleteAlert}
|
||||||
|
onLeftPress={() => setDeleteAlert(false)}
|
||||||
|
onRightPress={() => {
|
||||||
|
setDeleteAlert(false);
|
||||||
|
console.log("Hapus portofolio");
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
title="Hapus Portofolio"
|
||||||
|
message="Apakah Anda yakin ingin menghapus portofolio ini?"
|
||||||
|
textLeft="Batal"
|
||||||
|
textRight="Hapus"
|
||||||
|
colorRight={MainColor.red}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,14 +103,7 @@ export default function CreateProfile() {
|
|||||||
required
|
required
|
||||||
onChange={(value) => setData({ ...(data as any), gender: value })}
|
onChange={(value) => setData({ ...(data as any), gender: value })}
|
||||||
/>
|
/>
|
||||||
<TextInputCustom
|
<Spacing />
|
||||||
required
|
|
||||||
label="Alamat"
|
|
||||||
placeholder="Masukkan alamat"
|
|
||||||
value={data.address}
|
|
||||||
onChangeText={(text) => setData({ ...data, address: text })}
|
|
||||||
/>
|
|
||||||
{/* <Spacing /> */}
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,15 +22,8 @@ export default function ProfileEdit() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{ label: "React", value: "react" },
|
{ label: "Laki-laki", value: "laki-laki" },
|
||||||
{ label: "Vue", value: "vue" },
|
{ label: "Perempuan", value: "perempuan" },
|
||||||
{ label: "Angular", value: "angular" },
|
|
||||||
{ label: "Svelte", value: "svelte" },
|
|
||||||
{ label: "Next.js", value: "nextjs" },
|
|
||||||
{ label: "Nuxt.js", value: "nuxtjs" },
|
|
||||||
{ label: "Remix", value: "remix" },
|
|
||||||
{ label: "Sapper", value: "sapper" },
|
|
||||||
{ label: "SvelteKit", value: "sveltekit" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
@@ -59,16 +52,6 @@ export default function ProfileEdit() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<SelectCustom
|
|
||||||
label="Framework"
|
|
||||||
placeholder="Pilih framework favoritmu"
|
|
||||||
data={options}
|
|
||||||
value={data.selectedValue}
|
|
||||||
onChange={(value) => {
|
|
||||||
setData({ ...(data as any), selectedValue: value });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nama"
|
label="Nama"
|
||||||
placeholder="Nama"
|
placeholder="Nama"
|
||||||
@@ -96,6 +79,16 @@ export default function ProfileEdit() {
|
|||||||
}}
|
}}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
<SelectCustom
|
||||||
|
required
|
||||||
|
label="Jenis Kelamin"
|
||||||
|
placeholder="Pilih jenis kelamin"
|
||||||
|
data={options}
|
||||||
|
value={data.selectedValue}
|
||||||
|
onChange={(value) => {
|
||||||
|
setData({ ...(data as any), selectedValue: value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import LeftButtonCustom from "@/components/Button/BackButton";
|
|||||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
||||||
import Profile_MenuDrawerSection from "@/screens/Profile/MenuDrawerSection";
|
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
|
||||||
import ProfilSection from "@/screens/Profile/ProfilSection";
|
import ProfileSection from "@/screens/Profile/ProfileSection";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||||
@@ -52,7 +52,7 @@ export default function Profile() {
|
|||||||
headerTitleStyle: GStyles.headerTitleStyle,
|
headerTitleStyle: GStyles.headerTitleStyle,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ProfilSection />
|
<ProfileSection />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
{/* Drawer Komponen Eksternal */}
|
{/* Drawer Komponen Eksternal */}
|
||||||
|
|||||||
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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
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
@@ -5,6 +5,7 @@
|
|||||||
"name": "hipmi-mobile",
|
"name": "hipmi-mobile",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^14.1.0",
|
"@expo/vector-icons": "^14.1.0",
|
||||||
|
"@react-native-community/datetimepicker": "8.4.1",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.2",
|
"@react-navigation/bottom-tabs": "^7.4.2",
|
||||||
"@react-navigation/drawer": "^7.5.2",
|
"@react-navigation/drawer": "^7.5.2",
|
||||||
"@react-navigation/elements": "^2.3.8",
|
"@react-navigation/elements": "^2.3.8",
|
||||||
@@ -372,6 +373,8 @@
|
|||||||
|
|
||||||
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
"@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w=="],
|
||||||
|
|
||||||
|
"@react-native-community/datetimepicker": ["@react-native-community/datetimepicker@8.4.1", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": ">=52.0.0", "react": "*", "react-native": "*", "react-native-windows": "*" }, "optionalPeers": ["expo", "react-native-windows"] }, "sha512-DrK+CUS5fZnz8dhzBezirkzQTcNDdaXer3oDLh0z4nc2tbdIdnzwvXCvi8IEOIvleoc9L95xS5tKUl0/Xv71Mg=="],
|
||||||
|
|
||||||
"@react-native/assets-registry": ["@react-native/assets-registry@0.79.5", "", {}, "sha512-N4Kt1cKxO5zgM/BLiyzuuDNquZPiIgfktEQ6TqJ/4nKA8zr4e8KJgU6Tb2eleihDO4E24HmkvGc73naybKRz/w=="],
|
"@react-native/assets-registry": ["@react-native/assets-registry@0.79.5", "", {}, "sha512-N4Kt1cKxO5zgM/BLiyzuuDNquZPiIgfktEQ6TqJ/4nKA8zr4e8KJgU6Tb2eleihDO4E24HmkvGc73naybKRz/w=="],
|
||||||
|
|
||||||
"@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.79.5", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@react-native/codegen": "0.79.5" } }, "sha512-Rt/imdfqXihD/sn0xnV4flxxb1aLLjPtMF1QleQjEhJsTUPpH4TFlfOpoCvsrXoDl4OIcB1k4FVM24Ez92zf5w=="],
|
"@react-native/babel-plugin-codegen": ["@react-native/babel-plugin-codegen@0.79.5", "", { "dependencies": { "@babel/traverse": "^7.25.3", "@react-native/codegen": "0.79.5" } }, "sha512-Rt/imdfqXihD/sn0xnV4flxxb1aLLjPtMF1QleQjEhJsTUPpH4TFlfOpoCvsrXoDl4OIcB1k4FVM24Ez92zf5w=="],
|
||||||
|
|||||||
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?.(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -1,51 +1,71 @@
|
|||||||
import { AccentColor } from "@/constants/color-palet";
|
import { AccentColor } from "@/constants/color-palet";
|
||||||
import { PADDING_EXTRA_SMALL, PADDING_MEDIUM, PADDING_SMALL } from "@/constants/constans-value";
|
import {
|
||||||
import { StyleProp, TouchableHighlight, View, ViewStyle } from "react-native";
|
PADDING_MEDIUM,
|
||||||
|
PADDING_SMALL
|
||||||
|
} from "@/constants/constans-value";
|
||||||
|
import { Href, router } from "expo-router";
|
||||||
|
import {
|
||||||
|
StyleProp,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
ViewStyle
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
interface BaseBoxProps {
|
interface BaseBoxProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
|
href?: Href;
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
marginBottom?: number;
|
marginBottom?: number;
|
||||||
padding?: number;
|
padding?: number;
|
||||||
|
paddingTop?: number;
|
||||||
|
paddingBottom?: number;
|
||||||
paddingInline?: number;
|
paddingInline?: number;
|
||||||
paddingBlock?: number;
|
paddingBlock?: number;
|
||||||
|
backgroundColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BaseBox({
|
export default function BaseBox({
|
||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
|
href,
|
||||||
onPress,
|
onPress,
|
||||||
marginBottom = PADDING_MEDIUM,
|
marginBottom = PADDING_MEDIUM,
|
||||||
paddingBlock = PADDING_EXTRA_SMALL,
|
paddingBlock = PADDING_MEDIUM,
|
||||||
paddingInline = PADDING_SMALL,
|
paddingInline = PADDING_SMALL,
|
||||||
|
paddingTop = PADDING_MEDIUM,
|
||||||
|
paddingBottom = PADDING_MEDIUM,
|
||||||
|
backgroundColor = AccentColor.darkblue,
|
||||||
}: BaseBoxProps) {
|
}: BaseBoxProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{onPress ? (
|
{onPress || href ? (
|
||||||
<TouchableHighlight
|
<TouchableOpacity
|
||||||
onPress={onPress}
|
activeOpacity={0.7}
|
||||||
|
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,
|
||||||
marginBottom,
|
marginBottom,
|
||||||
paddingBlock,
|
paddingBlock,
|
||||||
paddingInline,
|
paddingInline,
|
||||||
|
paddingTop,
|
||||||
|
paddingBottom,
|
||||||
},
|
},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
// activeOpacity={0.7}
|
|
||||||
>
|
>
|
||||||
<View>{children}</View>
|
<View>{children}</View>
|
||||||
</TouchableHighlight>
|
</TouchableOpacity>
|
||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
backgroundColor: AccentColor.darkblue,
|
backgroundColor,
|
||||||
borderColor: AccentColor.blue,
|
borderColor: AccentColor.blue,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
|
|||||||
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
205
components/DateInput/DataTimeAndroid.tsx
Normal file
205
components/DateInput/DataTimeAndroid.tsx
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
// DateTimeInput.tsx
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { GStyles } from "@/styles/global-styles";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import DateTimePicker, {
|
||||||
|
DateTimePickerEvent,
|
||||||
|
} from "@react-native-community/datetimepicker";
|
||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { Pressable, StyleProp, Text, View, ViewStyle } from "react-native";
|
||||||
|
import Grid from "../Grid/GridCustom";
|
||||||
|
import TextCustom from "../Text/TextCustom";
|
||||||
|
|
||||||
|
interface DateTimeInputProps {
|
||||||
|
// Main
|
||||||
|
value?: DateTimePickerEvent;
|
||||||
|
mode?: "date" | "time";
|
||||||
|
onChange: (selectedDate: DateTimePickerEvent) => void;
|
||||||
|
maximumDate?: Date;
|
||||||
|
minimumDate?: Date;
|
||||||
|
// Main
|
||||||
|
label?: string;
|
||||||
|
required?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
iconLeft?: React.ReactNode;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
borderRadius?: number;
|
||||||
|
externalError?: string;
|
||||||
|
internalError?: string;
|
||||||
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
|
||||||
|
// Main
|
||||||
|
value,
|
||||||
|
mode,
|
||||||
|
onChange,
|
||||||
|
maximumDate,
|
||||||
|
minimumDate,
|
||||||
|
// Main
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
disabled,
|
||||||
|
iconLeft,
|
||||||
|
style,
|
||||||
|
borderRadius = 8,
|
||||||
|
externalError,
|
||||||
|
internalError,
|
||||||
|
containerStyle,
|
||||||
|
}) => {
|
||||||
|
const [showDate, setShowDate] = useState(false);
|
||||||
|
const [showTime, setShowTime] = useState(false);
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Date>(value as any);
|
||||||
|
const [selectedTime, setSelectedTime] = useState<Date>(value as any);
|
||||||
|
|
||||||
|
// Fungsi untuk menggabungkan tanggal dan waktu
|
||||||
|
const combineDateAndTime = useCallback((date: Date, time: Date): Date => {
|
||||||
|
const combined = new Date(date);
|
||||||
|
combined.setHours(
|
||||||
|
time.getHours(),
|
||||||
|
time.getMinutes(),
|
||||||
|
time.getSeconds(),
|
||||||
|
time.getMilliseconds()
|
||||||
|
);
|
||||||
|
return combined;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Handler untuk tanggal
|
||||||
|
const handleConfirmDate = (event: DateTimePickerEvent, date?: Date) => {
|
||||||
|
if (event.type === "set" && date) {
|
||||||
|
setSelectedDate(date);
|
||||||
|
if (selectedTime) {
|
||||||
|
const combined = combineDateAndTime(date, selectedTime);
|
||||||
|
onChange?.(combined as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShowDate(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler untuk waktu
|
||||||
|
const handleConfirmTime = (event: DateTimePickerEvent, time?: Date) => {
|
||||||
|
if (event.type === "set" && time) {
|
||||||
|
setSelectedTime(time);
|
||||||
|
if (selectedDate) {
|
||||||
|
const combined = combineDateAndTime(selectedDate, time);
|
||||||
|
onChange?.(combined as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShowTime(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDatePicker = () => {
|
||||||
|
setShowDate(!showDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTimePicker = () => {
|
||||||
|
setShowTime(!showTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<View style={[GStyles.inputContainerArea, containerStyle]}>
|
||||||
|
{label && (
|
||||||
|
<Text style={GStyles.inputLabel}>
|
||||||
|
{label}
|
||||||
|
{required && <Text style={GStyles.inputRequired}> *</Text>}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
style,
|
||||||
|
{ borderRadius },
|
||||||
|
externalError || internalError ? GStyles.inputErrorBorder : null,
|
||||||
|
GStyles.inputContainerInput,
|
||||||
|
disabled && GStyles.disabledBox,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={GStyles.inputIcon}>
|
||||||
|
<Ionicons
|
||||||
|
name="calendar-outline"
|
||||||
|
size={20}
|
||||||
|
color={MainColor.placeholder}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
containerStyle={{
|
||||||
|
borderRadius: 8,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid.Col span={6} style={{}}>
|
||||||
|
<Pressable onPress={toggleDatePicker}>
|
||||||
|
<TextCustom color="gray">
|
||||||
|
{selectedDate ? (
|
||||||
|
<TextCustom color="black">
|
||||||
|
{selectedDate.toLocaleDateString()}
|
||||||
|
</TextCustom>
|
||||||
|
) : (
|
||||||
|
"Pilih tanggal"
|
||||||
|
)}
|
||||||
|
</TextCustom>
|
||||||
|
</Pressable>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={1} style={{ alignItems: "center" }}>
|
||||||
|
<TextCustom color="gray">|</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
|
||||||
|
<Grid.Col span={5} style={{}}>
|
||||||
|
<Pressable onPress={toggleTimePicker}>
|
||||||
|
<TextCustom color="gray">
|
||||||
|
{selectedTime ? (
|
||||||
|
<TextCustom color="black">
|
||||||
|
{selectedTime.toLocaleTimeString("id-ID", {
|
||||||
|
minute: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
})}
|
||||||
|
</TextCustom>
|
||||||
|
) : (
|
||||||
|
"Pilih waktu"
|
||||||
|
)}
|
||||||
|
</TextCustom>
|
||||||
|
</Pressable>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</View>
|
||||||
|
{externalError ||
|
||||||
|
(internalError && (
|
||||||
|
<Text style={GStyles.inputErrorMessage}>
|
||||||
|
{externalError || internalError}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{showDate && (
|
||||||
|
<DateTimePicker
|
||||||
|
testID="dateTimePicker"
|
||||||
|
value={selectedDate || new Date()}
|
||||||
|
mode="date"
|
||||||
|
is24Hour={true}
|
||||||
|
display="default"
|
||||||
|
onChange={handleConfirmDate}
|
||||||
|
minimumDate={minimumDate}
|
||||||
|
maximumDate={maximumDate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showTime && (
|
||||||
|
<DateTimePicker
|
||||||
|
testID="dateTimePicker"
|
||||||
|
value={selectedTime || new Date()}
|
||||||
|
mode="time"
|
||||||
|
is24Hour={true}
|
||||||
|
display="default"
|
||||||
|
onChange={handleConfirmTime}
|
||||||
|
minimumDate={minimumDate}
|
||||||
|
maximumDate={maximumDate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateTimeInput_Android;
|
||||||
161
components/DateInput/DateTimeIOS.tsx
Normal file
161
components/DateInput/DateTimeIOS.tsx
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// DateTimeInput.tsx
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { GStyles } from "@/styles/global-styles";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import DateTimePicker, {
|
||||||
|
DateTimePickerEvent,
|
||||||
|
} from "@react-native-community/datetimepicker";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
StyleProp,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
ViewStyle
|
||||||
|
} from "react-native";
|
||||||
|
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||||
|
import TextCustom from "../Text/TextCustom";
|
||||||
|
|
||||||
|
interface DateTimeInputProps {
|
||||||
|
// Main
|
||||||
|
value?: DateTimePickerEvent;
|
||||||
|
mode?: "date" | "time";
|
||||||
|
onChange: (selectedDate: DateTimePickerEvent) => void;
|
||||||
|
maximumDate?: Date;
|
||||||
|
minimumDate?: Date;
|
||||||
|
// Main
|
||||||
|
label?: string;
|
||||||
|
required?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
iconLeft?: React.ReactNode;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
borderRadius?: number;
|
||||||
|
externalError?: string;
|
||||||
|
internalError?: string;
|
||||||
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
|
||||||
|
// Main
|
||||||
|
value,
|
||||||
|
mode,
|
||||||
|
onChange,
|
||||||
|
maximumDate,
|
||||||
|
minimumDate,
|
||||||
|
// Main
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
disabled,
|
||||||
|
iconLeft,
|
||||||
|
style,
|
||||||
|
borderRadius = 8,
|
||||||
|
externalError,
|
||||||
|
internalError,
|
||||||
|
containerStyle,
|
||||||
|
}) => {
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Date | undefined>(
|
||||||
|
value as any
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleConfirm = (event: any, date?: Date) => {
|
||||||
|
if (event.type === "set" && date !== undefined) {
|
||||||
|
setSelectedDate(date);
|
||||||
|
onChange(date as any);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePress = () => {
|
||||||
|
setShow(!show);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ClickableCustom
|
||||||
|
activeOpacity={0.8}
|
||||||
|
style={[GStyles.inputContainerArea, containerStyle]}
|
||||||
|
onPress={handlePress}
|
||||||
|
>
|
||||||
|
{label && (
|
||||||
|
<Text style={GStyles.inputLabel}>
|
||||||
|
{label}
|
||||||
|
{required && <Text style={GStyles.inputRequired}> *</Text>}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
style,
|
||||||
|
{ borderRadius },
|
||||||
|
externalError || internalError ? GStyles.inputErrorBorder : null,
|
||||||
|
GStyles.inputContainerInput,
|
||||||
|
disabled && GStyles.disabledBox,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<View style={GStyles.inputIcon}>
|
||||||
|
<Ionicons
|
||||||
|
name="calendar-outline"
|
||||||
|
size={20}
|
||||||
|
color={MainColor.placeholder}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TextCustom color="gray">
|
||||||
|
{selectedDate ? (
|
||||||
|
<TextCustom color="black">
|
||||||
|
{dayjs(selectedDate).format("DD-MM-YYYY HH:mm")}
|
||||||
|
</TextCustom>
|
||||||
|
) : (
|
||||||
|
"Pilih tanggal"
|
||||||
|
)}
|
||||||
|
</TextCustom>
|
||||||
|
</View>
|
||||||
|
{externalError ||
|
||||||
|
(internalError && (
|
||||||
|
<Text style={GStyles.inputErrorMessage}>
|
||||||
|
{externalError || internalError}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
</ClickableCustom>
|
||||||
|
|
||||||
|
{show && (
|
||||||
|
<>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 15,
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 10,
|
||||||
|
// top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
borderColor: "#ccc",
|
||||||
|
borderWidth: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ alignItems: "flex-end" }}>
|
||||||
|
<Ionicons
|
||||||
|
name="close"
|
||||||
|
size={20}
|
||||||
|
color="black"
|
||||||
|
onPress={() => setShow(false)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<DateTimePicker
|
||||||
|
value={selectedDate || new Date()}
|
||||||
|
mode={"datetime"}
|
||||||
|
display="inline"
|
||||||
|
onChange={handleConfirm}
|
||||||
|
minimumDate={minimumDate}
|
||||||
|
maximumDate={maximumDate}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateTimeInput_IOS;
|
||||||
54
components/DateInput/DateTimePickerCustom.tsx
Normal file
54
components/DateInput/DateTimePickerCustom.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
DateTimePickerEvent,
|
||||||
|
} from "@react-native-community/datetimepicker";
|
||||||
|
import React from "react";
|
||||||
|
import { Platform } from "react-native";
|
||||||
|
import DateTimeInput_Android from "./DataTimeAndroid";
|
||||||
|
import DateTimeInput_IOS from "./DateTimeIOS";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value?: Date;
|
||||||
|
onChange?: (date: Date) => void;
|
||||||
|
label?: string;
|
||||||
|
required?: boolean;
|
||||||
|
maximumDate?: Date;
|
||||||
|
minimumDate?: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateTimePickerCustom: React.FC<Props> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
label,
|
||||||
|
required,
|
||||||
|
maximumDate,
|
||||||
|
minimumDate,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Platform.OS === "ios" ? (
|
||||||
|
<DateTimeInput_IOS
|
||||||
|
label={label}
|
||||||
|
onChange={(date: DateTimePickerEvent) => {
|
||||||
|
onChange?.(date as any);
|
||||||
|
}}
|
||||||
|
required={required}
|
||||||
|
maximumDate={maximumDate}
|
||||||
|
minimumDate={minimumDate}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<DateTimeInput_Android
|
||||||
|
label={label}
|
||||||
|
onChange={(date: DateTimePickerEvent) => {
|
||||||
|
onChange?.(date as any);
|
||||||
|
}}
|
||||||
|
required={required}
|
||||||
|
maximumDate={maximumDate}
|
||||||
|
minimumDate={minimumDate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateTimePickerCustom;
|
||||||
70
components/DateInput/DateTimeTry.tsx
Normal file
70
components/DateInput/DateTimeTry.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Pressable, Text, StyleSheet } from "react-native";
|
||||||
|
import DateTimePicker, { Event } from "@react-native-community/datetimepicker";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value?: Date;
|
||||||
|
mode?: "date" | "time" | "datetime";
|
||||||
|
onChange: (date: Date) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateTimePickerTry: React.FC<Props> = ({
|
||||||
|
value = new Date(),
|
||||||
|
mode = "date",
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const [show, setShow] = useState(false);
|
||||||
|
|
||||||
|
const toggleDatePicker = () => {
|
||||||
|
setShow(!show);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirm = (event: Event, selectedDate?: Date) => {
|
||||||
|
if (event.type === "set" && selectedDate !== undefined) {
|
||||||
|
onChange(selectedDate);
|
||||||
|
}
|
||||||
|
setShow(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Pressable onPress={toggleDatePicker} style={styles.button}>
|
||||||
|
<Text style={styles.buttonText}>
|
||||||
|
{value ? value.toLocaleDateString() : "Pilih tanggal"}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
|
||||||
|
{show && (
|
||||||
|
<DateTimePicker
|
||||||
|
// style={styles.button}
|
||||||
|
textColor="white"
|
||||||
|
testID="dateTimePicker"
|
||||||
|
value={value}
|
||||||
|
mode={mode}
|
||||||
|
is24Hour={true}
|
||||||
|
display="default"
|
||||||
|
onChange={handleConfirm as any}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 8,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
marginVertical: 10,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: "white",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DateTimePickerTry;
|
||||||
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}%` }]}
|
||||||
|
|||||||
132
components/Radio/RadioCustom.tsx
Normal file
132
components/Radio/RadioCustom.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// components/Radio.tsx
|
||||||
|
import { GStyles } from '@/styles/global-styles';
|
||||||
|
import React, { createContext, useCallback, useContext } from 'react';
|
||||||
|
import {
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TextStyle,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
ViewStyle
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Types
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
type RadioContextType = {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RadioContext = createContext<RadioContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
interface RadioGroupProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadioProps {
|
||||||
|
label: string;
|
||||||
|
value: string | number;
|
||||||
|
disabled?: boolean;
|
||||||
|
style?: ViewStyle;
|
||||||
|
labelStyle?: TextStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Components
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
export const RadioGroup: React.FC<RadioGroupProps> = ({ value, onChange, children, style }) => {
|
||||||
|
const contextValue = {
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <RadioContext.Provider value={contextValue}>{children}</RadioContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RadioCustom: React.FC<RadioProps> = ({ label, value, disabled = false, style, labelStyle }) => {
|
||||||
|
const context = useContext(RadioContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('Radio must be used inside a RadioGroup');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value: selectedValue, onChange } = context;
|
||||||
|
|
||||||
|
const handlePress = useCallback(() => {
|
||||||
|
if (!disabled && selectedValue !== value) {
|
||||||
|
onChange(value as any);
|
||||||
|
}
|
||||||
|
}, [disabled, selectedValue, value, onChange]);
|
||||||
|
|
||||||
|
const isSelected = selectedValue === value;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.radioContainer, style]}
|
||||||
|
onPress={handlePress}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{/* Circle */}
|
||||||
|
<View
|
||||||
|
style={[styles.outerCircle, isSelected && styles.outerCircleChecked]}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={[styles.innerCircle, isSelected && styles.innerCircleChecked]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Label */}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
GStyles.textLabelBold,
|
||||||
|
labelStyle,
|
||||||
|
disabled && GStyles.inputTextDisabled,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Styles
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
radioContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginVertical: 8,
|
||||||
|
},
|
||||||
|
outerCircle: {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
borderRadius: 12,
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: '#3B82F6',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
outerCircleChecked: {
|
||||||
|
backgroundColor: '#3B82F6',
|
||||||
|
},
|
||||||
|
innerCircle: {
|
||||||
|
height: 12,
|
||||||
|
width: 12,
|
||||||
|
borderRadius: 6,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
innerCircleChecked: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderColor: 'white',
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import ButtonCustom from "../Button/ButtonCustom";
|
|||||||
interface ButtonData {
|
interface ButtonData {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
label: string;
|
label: string;
|
||||||
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScrollableCustomProps {
|
interface ScrollableCustomProps {
|
||||||
@@ -27,7 +28,7 @@ const ScrollableCustom = ({
|
|||||||
style={styles.scrollView}
|
style={styles.scrollView}
|
||||||
>
|
>
|
||||||
{data.map((item) => {
|
{data.map((item) => {
|
||||||
const isActive = activeId === item.id;
|
const isActive = activeId === item.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
@@ -48,6 +49,9 @@ export default ScrollableCustom;
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scrollView: {
|
scrollView: {
|
||||||
|
backgroundColor: MainColor.soft_darkblue,
|
||||||
|
borderRadius: 50,
|
||||||
|
padding: 5,
|
||||||
// maxHeight: 50,
|
// maxHeight: 50,
|
||||||
},
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
TEXT_SIZE_LARGE,
|
TEXT_SIZE_LARGE,
|
||||||
TEXT_SIZE_MEDIUM,
|
TEXT_SIZE_MEDIUM,
|
||||||
TEXT_SIZE_SMALL,
|
TEXT_SIZE_SMALL,
|
||||||
|
TEXT_SIZE_XLARGE,
|
||||||
} from "@/constants/constans-value";
|
} from "@/constants/constans-value";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
@@ -21,8 +22,8 @@ interface TextCustomProps {
|
|||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
bold?: boolean;
|
bold?: boolean;
|
||||||
semiBold?: boolean;
|
semiBold?: boolean;
|
||||||
size?: "default" | "large" | "small";
|
size?: "default" | "large" | "small" | "xlarge";
|
||||||
color?: "default" | "yellow" | "red" | "gray" | "green";
|
color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
|
||||||
align?: TextAlign; // Prop untuk alignment
|
align?: TextAlign; // Prop untuk alignment
|
||||||
truncate?: boolean | number;
|
truncate?: boolean | number;
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
@@ -51,6 +52,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
|||||||
|
|
||||||
// Size
|
// Size
|
||||||
if (size === "large") selectedStyles.push(styles.large);
|
if (size === "large") selectedStyles.push(styles.large);
|
||||||
|
else if (size === "xlarge") selectedStyles.push(styles.xlarge);
|
||||||
else if (size === "small") selectedStyles.push(styles.small);
|
else if (size === "small") selectedStyles.push(styles.small);
|
||||||
|
|
||||||
// Color
|
// Color
|
||||||
@@ -58,6 +60,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
|||||||
else if (color === "red") selectedStyles.push(styles.red);
|
else if (color === "red") selectedStyles.push(styles.red);
|
||||||
else if (color === "gray") selectedStyles.push(styles.gray);
|
else if (color === "gray") selectedStyles.push(styles.gray);
|
||||||
else if (color === "green") selectedStyles.push(styles.green);
|
else if (color === "green") selectedStyles.push(styles.green);
|
||||||
|
else if (color === "black") selectedStyles.push(styles.black);
|
||||||
|
|
||||||
// Alignment
|
// Alignment
|
||||||
if (align) {
|
if (align) {
|
||||||
@@ -112,11 +115,14 @@ export const styles = StyleSheet.create({
|
|||||||
fontFamily: "Poppins-SemiBold",
|
fontFamily: "Poppins-SemiBold",
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
},
|
},
|
||||||
|
small: {
|
||||||
|
fontSize: TEXT_SIZE_SMALL,
|
||||||
|
},
|
||||||
large: {
|
large: {
|
||||||
fontSize: TEXT_SIZE_LARGE,
|
fontSize: TEXT_SIZE_LARGE,
|
||||||
},
|
},
|
||||||
small: {
|
xlarge: {
|
||||||
fontSize: TEXT_SIZE_SMALL,
|
fontSize: TEXT_SIZE_XLARGE,
|
||||||
},
|
},
|
||||||
yellow: {
|
yellow: {
|
||||||
color: MainColor.yellow,
|
color: MainColor.yellow,
|
||||||
@@ -130,4 +136,7 @@ export const styles = StyleSheet.create({
|
|||||||
green: {
|
green: {
|
||||||
color: MainColor.green,
|
color: MainColor.green,
|
||||||
},
|
},
|
||||||
|
black: {
|
||||||
|
color: MainColor.black,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
TextInput as RNTextInput,
|
TextInput as RNTextInput,
|
||||||
StyleProp,
|
StyleProp,
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
|
||||||
type IconType = React.ReactNode | string;
|
type IconType = React.ReactNode | string;
|
||||||
@@ -79,7 +79,7 @@ const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={GStyles.inputContainerArea}>
|
<View style={[GStyles.inputContainerArea]}>
|
||||||
{label && (
|
{label && (
|
||||||
<Text style={GStyles.inputLabel}>
|
<Text style={GStyles.inputLabel}>
|
||||||
{label}
|
{label}
|
||||||
@@ -105,7 +105,7 @@ const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
|
|||||||
multiline
|
multiline
|
||||||
numberOfLines={numberOfLines}
|
numberOfLines={numberOfLines}
|
||||||
style={[
|
style={[
|
||||||
GStyles.inputText,
|
// GStyles.inputText,
|
||||||
GStyles.textAreaInput,
|
GStyles.textAreaInput,
|
||||||
{ color: fontColor },
|
{ color: fontColor },
|
||||||
]}
|
]}
|
||||||
|
|||||||
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 };
|
||||||
@@ -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;
|
||||||
@@ -3,12 +3,12 @@ import React from "react";
|
|||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
interface SpacingProps {
|
interface SpacingProps {
|
||||||
width?: number;
|
width?: number | string;
|
||||||
height?: number;
|
height?: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Spacing: React.FC<SpacingProps> = ({ width = 20, height = 20 }) => {
|
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;
|
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,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { OS_HEIGHT } from "@/constants/constans-value";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import {
|
import {
|
||||||
ImageBackground,
|
ImageBackground,
|
||||||
@@ -19,15 +20,23 @@ interface ViewWrapperProps {
|
|||||||
headerComponent?: React.ReactNode;
|
headerComponent?: React.ReactNode;
|
||||||
footerComponent?: React.ReactNode;
|
footerComponent?: React.ReactNode;
|
||||||
floatingButton?: React.ReactNode;
|
floatingButton?: React.ReactNode;
|
||||||
|
hideFooter?: boolean;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param hideFooter
|
||||||
|
* @returns meneyembunyikan footer ketika menggunakan tabs (misal: bottom tab)
|
||||||
|
*/
|
||||||
|
|
||||||
const ViewWrapper = ({
|
const ViewWrapper = ({
|
||||||
children,
|
children,
|
||||||
withBackground = false,
|
withBackground = false,
|
||||||
headerComponent,
|
headerComponent,
|
||||||
footerComponent,
|
footerComponent,
|
||||||
floatingButton,
|
floatingButton,
|
||||||
|
hideFooter = false,
|
||||||
style,
|
style,
|
||||||
}: ViewWrapperProps) => {
|
}: ViewWrapperProps) => {
|
||||||
const assetBackground = require("../../assets/images/main-background.png");
|
const assetBackground = require("../../assets/images/main-background.png");
|
||||||
@@ -71,15 +80,18 @@ const ViewWrapper = ({
|
|||||||
edges={["bottom"]}
|
edges={["bottom"]}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: MainColor.darkblue,
|
backgroundColor: MainColor.darkblue,
|
||||||
|
height: OS_HEIGHT
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{footerComponent}
|
{footerComponent}
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
) : (
|
) : (
|
||||||
<SafeAreaView
|
hideFooter ? null : (
|
||||||
edges={["bottom"]}
|
<SafeAreaView
|
||||||
style={{ backgroundColor: MainColor.darkblue }}
|
edges={["bottom"]}
|
||||||
/>
|
style={{ backgroundColor: MainColor.darkblue }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Floating Component (misal: FAB) */}
|
{/* Floating Component (misal: FAB) */}
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
// 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 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";
|
||||||
// ShareComponent
|
|
||||||
import Spacing from "./_ShareComponent/Spacing";
|
|
||||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
|
||||||
// Text
|
// Text
|
||||||
import TextCustom from "./Text/TextCustom";
|
import TextCustom from "./Text/TextCustom";
|
||||||
// TextInput
|
// TextInput
|
||||||
@@ -21,6 +24,7 @@ import Grid from "./Grid/GridCustom";
|
|||||||
// Box
|
// Box
|
||||||
import BaseBox from "./Box/BaseBox";
|
import BaseBox from "./Box/BaseBox";
|
||||||
import BoxButtonOnFooter from "./Box/BoxButtonOnFooter";
|
import BoxButtonOnFooter from "./Box/BoxButtonOnFooter";
|
||||||
|
import BoxWithHeaderSection from "./Box/BoxWithHeaderInformation";
|
||||||
import InformationBox from "./Box/InformationBox";
|
import InformationBox from "./Box/InformationBox";
|
||||||
// Stack
|
// Stack
|
||||||
import StackCustom from "./Stack/StackCustom";
|
import StackCustom from "./Stack/StackCustom";
|
||||||
@@ -30,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";
|
||||||
@@ -39,33 +44,57 @@ import CenterCustom from "./Center/CenterCustom";
|
|||||||
import ClickableCustom from "./Clickable/ClickableCustom";
|
import ClickableCustom from "./Clickable/ClickableCustom";
|
||||||
// Scroll
|
// Scroll
|
||||||
import ScrollableCustom from "./Scroll/ScrollCustom";
|
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";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AlertCustom,
|
AlertCustom,
|
||||||
|
AlertDefaultSystem,
|
||||||
// Image
|
// Image
|
||||||
AvatarCustom,
|
AvatarCustom,
|
||||||
LandscapeFrameUploaded,
|
// ShareComponent
|
||||||
|
AvatarUsernameAndOtherComponent,
|
||||||
|
// Button
|
||||||
|
LeftButtonCustom as BackButton,
|
||||||
// Box
|
// Box
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
|
BoxWithHeaderSection,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
InformationBox,
|
|
||||||
LeftButtonCustom as BackButton,
|
|
||||||
// Button
|
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
|
DotButton,
|
||||||
|
// Center
|
||||||
|
CenterCustom,
|
||||||
|
// Checkbox
|
||||||
|
CheckboxCustom,
|
||||||
|
CheckboxGroup,
|
||||||
|
// Clickable
|
||||||
|
ClickableCustom,
|
||||||
|
// Divider
|
||||||
|
Divider,
|
||||||
|
DividerCustom,
|
||||||
// Drawer
|
// Drawer
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
MenuDrawerDynamicGrid,
|
FloatingButton,
|
||||||
// Grid
|
// Grid
|
||||||
Grid,
|
Grid,
|
||||||
|
InformationBox,
|
||||||
|
LandscapeFrameUploaded,
|
||||||
// Map
|
// Map
|
||||||
MapCustom,
|
MapCustom,
|
||||||
|
MenuDrawerDynamicGrid,
|
||||||
|
// Scroll
|
||||||
|
ScrollableCustom,
|
||||||
// Select
|
// Select
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
// ShareComponent
|
// ShareComponent
|
||||||
Spacing,
|
Spacing,
|
||||||
// Stack
|
// Stack
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TabBarBackground,
|
||||||
// TextArea
|
// TextArea
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
// Text
|
// Text
|
||||||
@@ -74,12 +103,4 @@ export {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
// ViewWrapper
|
// ViewWrapper
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
// Divider
|
|
||||||
DividerCustom,
|
|
||||||
// Center
|
|
||||||
CenterCustom,
|
|
||||||
// Clickable
|
|
||||||
ClickableCustom,
|
|
||||||
// Scroll
|
|
||||||
ScrollableCustom,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
|
import { Platform } from "react-native";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
OS_ANDROID_HEIGHT,
|
||||||
|
OS_IOS_HEIGHT,
|
||||||
|
OS_HEIGHT,
|
||||||
TEXT_SIZE_SMALL,
|
TEXT_SIZE_SMALL,
|
||||||
TEXT_SIZE_MEDIUM,
|
TEXT_SIZE_MEDIUM,
|
||||||
TEXT_SIZE_LARGE,
|
TEXT_SIZE_LARGE,
|
||||||
|
TEXT_SIZE_XLARGE,
|
||||||
ICON_SIZE_SMALL,
|
ICON_SIZE_SMALL,
|
||||||
ICON_SIZE_MEDIUM,
|
ICON_SIZE_MEDIUM,
|
||||||
DRAWER_HEIGHT,
|
DRAWER_HEIGHT,
|
||||||
@@ -13,10 +19,16 @@ export {
|
|||||||
PADDING_LARGE,
|
PADDING_LARGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// OS Height
|
||||||
|
const OS_ANDROID_HEIGHT = 115
|
||||||
|
const OS_IOS_HEIGHT = 65
|
||||||
|
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
|
||||||
|
|
||||||
// Text Size
|
// Text Size
|
||||||
const TEXT_SIZE_SMALL = 12;
|
const TEXT_SIZE_SMALL = 12;
|
||||||
const TEXT_SIZE_MEDIUM = 14;
|
const TEXT_SIZE_MEDIUM = 14;
|
||||||
const TEXT_SIZE_LARGE = 16;
|
const TEXT_SIZE_LARGE = 16;
|
||||||
|
const TEXT_SIZE_XLARGE = 18;
|
||||||
|
|
||||||
// Icon Size
|
// Icon Size
|
||||||
const ICON_SIZE_BUTTON = 18
|
const ICON_SIZE_BUTTON = 18
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const DUMMY_IMAGE = {
|
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"),
|
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" },
|
||||||
|
];
|
||||||
8
lib/dummy-data/event/master-type-event.tsx
Normal file
8
lib/dummy-data/event/master-type-event.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export const masterTypeEvent = [
|
||||||
|
{ label: "Seminar", value: "seminar" },
|
||||||
|
{ label: "Workshop", value: "workshop" },
|
||||||
|
{ label: "Lomba", value: "lomba" },
|
||||||
|
{ label: "Pameran", value: "pameran" },
|
||||||
|
{ label: "Kegiatan Sosial", value: "kegiatan_sosial" },
|
||||||
|
{ label: "Musyawarah", value: "musyawarah" },
|
||||||
|
];
|
||||||
26
lib/dummy-data/forum/report-list.tsx
Normal file
26
lib/dummy-data/forum/report-list.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
export const listDummyReportForum = [
|
||||||
|
{
|
||||||
|
title: "Kebencian",
|
||||||
|
desc: "Cercaan, Stereotip rasis atau seksis, Dehumanisasi, Menyulut ketakutan atau diskriminasi, Referensi kebencian, Simbol & logo kebencian",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Penghinaan & Pelecehan secara Online",
|
||||||
|
desc: "Penghinaan, Konten Seksual yang Tidak Diinginkan, Penyangkalan Peristiwa Kekerasan, Pelecehan Bertarget dan Memprovokasi Pelecehan",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tutur Kekerasan",
|
||||||
|
desc: "Ancaman Kekerasan, Berharap Terjadinya Celaka, Mengagungkan Kekerasan, Penghasutan Kekerasan, Penghasutan Kekerasan dengan Kode",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Keselamatan Anak",
|
||||||
|
desc: "Eksploitasi seks anak di bawah umur, grooming, kekerasan fisik terhadap anak, pengguna di bawah umur",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Privasi",
|
||||||
|
desc: "Membagikan informasi pribadi, mengancam akan membagikan/menyebarkan informasi pribadi, membagikan gambar intim tanpa persetujuan, membagikan gambar saya yang tidak saya kehendaki di platform ini",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Spam",
|
||||||
|
desc: "Akun palsu, penipuan keuangan, memposting tautan berbahaya, menyalahgunakan hashtag, keterlibatan palsu, balasan berulang, Posting Ulang, atau Direct Message",
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^14.1.0",
|
"@expo/vector-icons": "^14.1.0",
|
||||||
|
"@react-native-community/datetimepicker": "8.4.1",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.2",
|
"@react-navigation/bottom-tabs": "^7.4.2",
|
||||||
"@react-navigation/drawer": "^7.5.2",
|
"@react-navigation/drawer": "^7.5.2",
|
||||||
"@react-navigation/elements": "^2.3.8",
|
"@react-navigation/elements": "^2.3.8",
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export default function LoginView() {
|
|||||||
// 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)/coba");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import ButtonCustom from "@/components/Button/ButtonCustom";
|
import ButtonCustom from "@/components/Button/ButtonCustom";
|
||||||
import { TextInputCustom } from "@/components/TextInput/TextInputCustom";
|
import TextInputCustom from "@/components/TextInput/TextInputCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
|
|||||||
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,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
BaseBox,
|
|
||||||
Grid,
|
|
||||||
AvatarCustom,
|
AvatarCustom,
|
||||||
TextCustom,
|
BaseBox,
|
||||||
ClickableCustom,
|
ClickableCustom,
|
||||||
|
Grid,
|
||||||
Spacing,
|
Spacing,
|
||||||
|
TextCustom,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
@@ -14,11 +14,9 @@ import { View } from "react-native";
|
|||||||
export default function Forum_CommentarBoxSection({
|
export default function Forum_CommentarBoxSection({
|
||||||
data,
|
data,
|
||||||
setOpenDrawer,
|
setOpenDrawer,
|
||||||
setStatus,
|
|
||||||
}: {
|
}: {
|
||||||
data: any;
|
data: any;
|
||||||
setOpenDrawer: (value: boolean) => void;
|
setOpenDrawer: (value: boolean) => void;
|
||||||
setStatus: (value: string) => void;
|
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -46,7 +44,6 @@ export default function Forum_CommentarBoxSection({
|
|||||||
<ClickableCustom
|
<ClickableCustom
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setOpenDrawer(true);
|
setOpenDrawer(true);
|
||||||
setStatus(data.status);
|
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
alignItems: "flex-end",
|
alignItems: "flex-end",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { MainColor } from "@/constants/color-palet";
|
|||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
export { drawerItemsForumBeranda };
|
export { drawerItemsForumBeranda, drawerItemsForumComentar };
|
||||||
|
|
||||||
const drawerItemsForumBeranda = ({
|
const drawerItemsForumBeranda = ({
|
||||||
id,
|
id,
|
||||||
@@ -11,6 +11,15 @@ const drawerItemsForumBeranda = ({
|
|||||||
id: string;
|
id: string;
|
||||||
status: string;
|
status: string;
|
||||||
}) => [
|
}) => [
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<Ionicons name="flag" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
|
),
|
||||||
|
label: "Laporkan diskusi",
|
||||||
|
// color: MainColor.white,
|
||||||
|
path: `/forum/${id}/report-posting`,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<Feather name="edit" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
<Feather name="edit" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
@@ -21,14 +30,14 @@ const drawerItemsForumBeranda = ({
|
|||||||
{
|
{
|
||||||
icon:
|
icon:
|
||||||
status === "Open" ? (
|
status === "Open" ? (
|
||||||
<Ionicons name="open" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
|
||||||
) : (
|
|
||||||
<Ionicons name="close" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
<Ionicons name="close" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
|
) : (
|
||||||
|
<Ionicons name="open" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
),
|
),
|
||||||
|
|
||||||
label: status === "Open" ? "Buka forum" : "Tutup forum",
|
label: status === "Open" ? "Tutup forum" : "Buka forum",
|
||||||
path: "",
|
path: "",
|
||||||
color: status === "Open" ? MainColor.green : MainColor.orange,
|
color: status === "Open" ? MainColor.orange : MainColor.green,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
@@ -39,3 +48,22 @@ const drawerItemsForumBeranda = ({
|
|||||||
color: MainColor.red,
|
color: MainColor.red,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const drawerItemsForumComentar = ({ id }: { id: string }) => [
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<Ionicons name="flag" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
|
),
|
||||||
|
label: "Laporkan",
|
||||||
|
// color: MainColor.white,
|
||||||
|
path: `/forum/${id}/report-commentar`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<Ionicons name="trash" size={ICON_SIZE_SMALL} color={MainColor.white} />
|
||||||
|
),
|
||||||
|
label: "Hapus",
|
||||||
|
color: MainColor.red,
|
||||||
|
path: "",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { MenuDrawerDynamicGrid } from "@/components";
|
||||||
|
import { drawerItemsForumComentar } from "../ListPage";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function Forum_MenuDrawerCommentar({
|
||||||
|
id,
|
||||||
|
setShowDeleteAlert,
|
||||||
|
setIsDrawerOpen,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
setShowDeleteAlert: (value: boolean) => void;
|
||||||
|
setIsDrawerOpen: (value: boolean) => void;
|
||||||
|
}) {
|
||||||
|
const handlePress = (item: any) => {
|
||||||
|
if (item.label === "Hapus") {
|
||||||
|
setShowDeleteAlert(true);
|
||||||
|
} else {
|
||||||
|
router.push(item.path as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsDrawerOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={drawerItemsForumComentar({ id })}
|
||||||
|
columns={4}
|
||||||
|
onPressItem={handlePress}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
41
screens/Forum/ReportListSection.tsx
Normal file
41
screens/Forum/ReportListSection.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { BaseBox, ButtonCustom, StackCustom, TextCustom } from "@/components";
|
||||||
|
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { listDummyReportForum } from "@/lib/dummy-data/forum/report-list";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
|
||||||
|
export default function Forum_ReportListSection() {
|
||||||
|
const [value, setValue] = useState<any | number>("");
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom>
|
||||||
|
<RadioGroup value={value} onChange={setValue}>
|
||||||
|
{listDummyReportForum.map((e, i) => (
|
||||||
|
<View key={i}>
|
||||||
|
<RadioCustom
|
||||||
|
label={e.title}
|
||||||
|
// value={i}
|
||||||
|
value={e.title}
|
||||||
|
/>
|
||||||
|
<TextCustom>{e.desc}</TextCustom>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
{/* <ButtonCustom
|
||||||
|
backgroundColor={MainColor.red}
|
||||||
|
textColor={MainColor.white}
|
||||||
|
onPress={() => {
|
||||||
|
console.log("Report", value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Report
|
||||||
|
</ButtonCustom> */}
|
||||||
|
</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>
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ import { ButtonCustom } from "@/components";
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
export default function Portofolio_ButtonDelete() {
|
export default function Portofolio_ButtonDelete({
|
||||||
|
setShowDeleteAlert,
|
||||||
|
}: {
|
||||||
|
setShowDeleteAlert: (value: boolean) => void;
|
||||||
|
}) {
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
console.log("Delete");
|
setShowDeleteAlert(true);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ButtonCustom textColor={MainColor.white} iconLeft={<Ionicons name="trash-outline" size={20} color="white" />} onPress={handleDelete} backgroundColor={MainColor.red}>
|
<ButtonCustom textColor={MainColor.white} iconLeft={<Ionicons name="trash-outline" size={20} color="white" />} onPress={handleDelete} backgroundColor={MainColor.red}>
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import Portofolio_Data from "./DataPortofolio";
|
|||||||
import Portofolio_SocialMediaSection from "./SocialMediaSection";
|
import Portofolio_SocialMediaSection from "./SocialMediaSection";
|
||||||
import Portofolio_ButtonDelete from "./ButtonDelete";
|
import Portofolio_ButtonDelete from "./ButtonDelete";
|
||||||
|
|
||||||
export default function PorfofolioSection() {
|
export default function PorfofolioSection({
|
||||||
|
setShowDeleteAlert,
|
||||||
|
}: {
|
||||||
|
setShowDeleteAlert: (value: boolean) => void;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<Portofolio_Data />
|
<Portofolio_Data />
|
||||||
<Portofolio_BusinessLocation />
|
<Portofolio_BusinessLocation />
|
||||||
<Portofolio_SocialMediaSection />
|
<Portofolio_SocialMediaSection />
|
||||||
<Portofolio_ButtonDelete/>
|
<Portofolio_ButtonDelete setShowDeleteAlert={setShowDeleteAlert}/>
|
||||||
<Spacing/>
|
<Spacing/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { router, useLocalSearchParams } from "expo-router";
|
|||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import AvatarAndBackground from "./AvatarAndBackground";
|
import AvatarAndBackground from "./AvatarAndBackground";
|
||||||
|
|
||||||
export default function ProfilSection() {
|
export default function ProfileSection() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
|
||||||
const listData = [
|
const listData = [
|
||||||
@@ -44,17 +44,16 @@ export const GStyles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
floatingContainer: {
|
floatingContainer: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: 80,
|
bottom: 100,
|
||||||
right: 20,
|
right: 20,
|
||||||
zIndex: 8,
|
zIndex: 8,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Style saat disabled
|
// =============== Disabled Styles =============== //
|
||||||
disabledBox: {
|
disabledBox: {
|
||||||
backgroundColor: MainColor.disabled,
|
backgroundColor: MainColor.disabled,
|
||||||
borderColor: AccentColor.disabledBorder,
|
borderColor: AccentColor.disabledBorder,
|
||||||
},
|
},
|
||||||
|
|
||||||
inputDisabled: {
|
inputDisabled: {
|
||||||
backgroundColor: "#f0f0f0",
|
backgroundColor: "#f0f0f0",
|
||||||
borderColor: "#ddd",
|
borderColor: "#ddd",
|
||||||
@@ -65,7 +64,7 @@ export const GStyles = StyleSheet.create({
|
|||||||
inputPlaceholderDisabled: {
|
inputPlaceholderDisabled: {
|
||||||
color: "#444",
|
color: "#444",
|
||||||
},
|
},
|
||||||
// =============== Main Styles =============== //
|
// =============== Disabled Styles =============== //
|
||||||
|
|
||||||
// =============== AUTHENTICATION =============== //
|
// =============== AUTHENTICATION =============== //
|
||||||
authContainer: {
|
authContainer: {
|
||||||
@@ -94,6 +93,11 @@ export const GStyles = StyleSheet.create({
|
|||||||
color: MainColor.white_gray,
|
color: MainColor.white_gray,
|
||||||
fontWeight: "normal",
|
fontWeight: "normal",
|
||||||
},
|
},
|
||||||
|
textLabelBold: {
|
||||||
|
fontSize: TEXT_SIZE_MEDIUM,
|
||||||
|
color: MainColor.white_gray,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
// =============== TEXT & LABEL =============== //
|
// =============== TEXT & LABEL =============== //
|
||||||
|
|
||||||
// =============== STACK HEADER =============== //
|
// =============== STACK HEADER =============== //
|
||||||
@@ -284,8 +288,10 @@ export const GStyles = StyleSheet.create({
|
|||||||
// TextArea untuk tambahan
|
// TextArea untuk tambahan
|
||||||
textAreaInput: {
|
textAreaInput: {
|
||||||
textAlignVertical: "top",
|
textAlignVertical: "top",
|
||||||
padding: 5,
|
paddingTop: 10,
|
||||||
height: undefined, // biar multiline bebas tinggi
|
// height: undefined, // biar multiline bebas tinggi
|
||||||
|
height: 100,
|
||||||
|
width: "100%",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Select
|
// Select
|
||||||
|
|||||||
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