Compare commits
22 Commits
collaborat
...
invesment/
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e31df660a | |||
| cc35bc6907 | |||
| 2931f6ba55 | |||
| 7205cb0736 | |||
| 54fc2a3d02 | |||
| 56d074260e | |||
| b3be6a7f53 | |||
| c863c04fb4 | |||
| a43ddaa9d6 | |||
| 927db87749 | |||
| 8a514d2670 | |||
| 554428b7b4 | |||
| befbd1a47d | |||
| c9a1ac1db5 | |||
| 4bcfcb5f5a | |||
| f21ff744d3 | |||
| b18044f53f | |||
| 20258d1fe5 | |||
| 51d696128e | |||
| 1b1732c7d8 | |||
| 7528c449eb | |||
| ed87d4a3f3 |
@@ -1,6 +1,7 @@
|
||||
import { BackButton } from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
@@ -147,9 +148,208 @@ export default function UserLayout() {
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
{/* ========== End Collaboration Section ========= */}
|
||||
|
||||
{/* ========== Voting Section ========= */}
|
||||
<Stack.Screen
|
||||
name="voting/create"
|
||||
options={{
|
||||
title: "Tambah Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/(tabs)"
|
||||
options={{
|
||||
title: "Voting",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/[id]/list-of-contributor"
|
||||
options={{
|
||||
title: "Daftar Kontributor",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Voting Section ========= */}
|
||||
|
||||
{/* ========== Crowdfunding Section ========= */}
|
||||
<Stack.Screen
|
||||
name="crowdfunding/index"
|
||||
options={{
|
||||
title: "Crowdfunding",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Crowdfunding Section ========= */}
|
||||
|
||||
{/* ========== Investment Section ========= */}
|
||||
<Stack.Screen
|
||||
name="investment/(tabs)"
|
||||
options={{
|
||||
title: "Investasi",
|
||||
headerLeft: () => <BackButton path="/crowdfunding" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/create"
|
||||
options={{
|
||||
title: "Tambah Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/index"
|
||||
options={{
|
||||
title: "Detail Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/edit-prospectus"
|
||||
options={{
|
||||
title: "Edit Prospektus",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/list-of-document"
|
||||
options={{
|
||||
title: "Daftar Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/add-document"
|
||||
options={{
|
||||
title: "Tambah Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/edit-document"
|
||||
options={{
|
||||
title: "Edit Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(news)/add-news"
|
||||
options={{
|
||||
title: "Tambah Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/investor"
|
||||
options={{
|
||||
title: "Investor",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/index"
|
||||
options={{
|
||||
title: "Pembelian Saham",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/select-bank"
|
||||
options={{
|
||||
title: "Pilih Bank",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/invoice"
|
||||
options={{
|
||||
title: "Invoice",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/process"
|
||||
options={{
|
||||
title: "Proses",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate(`/investment/(tabs)/transaction`)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Investment Section ========= */}
|
||||
|
||||
{/* ========== Donation Section ========= */}
|
||||
<Stack.Screen
|
||||
name="donation/create"
|
||||
options={{
|
||||
title: "Tambah Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Donation Section ========= */}
|
||||
|
||||
{/* ========== Job Section ========= */}
|
||||
<Stack.Screen
|
||||
name="job/create"
|
||||
options={{
|
||||
title: "Tambah Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/(tabs)"
|
||||
options={{
|
||||
title: "Job Vacancy",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/index"
|
||||
options={{
|
||||
title: "Detail Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/edit"
|
||||
options={{
|
||||
title: "Edit Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Job Section ========= */}
|
||||
|
||||
{/* ========== Forum Section ========= */}
|
||||
<Stack.Screen
|
||||
name="forum/create"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { IconHome } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
@@ -9,9 +10,7 @@ export default function CollaborationTabsLayout() {
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="home" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
|
||||
68
app/(application)/(user)/crowdfunding/index.tsx
Normal file
68
app/(application)/(user)/crowdfunding/index.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
|
||||
export default function Crowdfunding() {
|
||||
const listPage = [
|
||||
{
|
||||
title: "Investasi",
|
||||
desc: "Buat investasi dan jual beli saham lebih mudah dengan pengguna lain.",
|
||||
path: "investment/(tabs)",
|
||||
},
|
||||
{
|
||||
title: "Donasi",
|
||||
desc: "Berbagi info untuk berdonasi lebih luas dan lebih efisien.",
|
||||
path: "donation/create",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom>
|
||||
<Image
|
||||
source={require("@/assets/images/constants/crowd-hipmi.png")}
|
||||
contentFit="cover"
|
||||
transition={1000}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: 200,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
/>
|
||||
|
||||
{listPage.map((item, index) => (
|
||||
<BaseBox key={index} paddingTop={10} paddingBottom={10} href={item.path as any} marginBottom={0}>
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom bold size="large">
|
||||
{item.title}
|
||||
</TextCustom>
|
||||
<TextCustom>{item.desc}</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
<Feather
|
||||
name="chevron-right"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
11
app/(application)/(user)/donation/create.tsx
Normal file
11
app/(application)/(user)/donation/create.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TextCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function DonationCreate() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<TextCustom bold size="large">
|
||||
Coming Soon !
|
||||
</TextCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,49 +1,43 @@
|
||||
import {
|
||||
IconContribution,
|
||||
IconHistory,
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function EventTabsLayout() {
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={TabsStyles}
|
||||
>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="home" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="list" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="extension-puzzle" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome5 size={20} name="history" color={color} />
|
||||
),
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,9 +73,9 @@ export default function EventDetailStatus() {
|
||||
height={250}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerDraftEvent({ id: id as string })}
|
||||
data={menuDrawerDraftEvent({ id: id as string }) as any}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@ import {
|
||||
AvatarCustom,
|
||||
BackButton,
|
||||
DrawerCustom,
|
||||
TextInputCustom,
|
||||
SearchInput,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDummyDiscussionForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -34,20 +32,7 @@ export default function Forum() {
|
||||
/>
|
||||
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari topik forum..."
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari topik diskusi" />}
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
|
||||
59
app/(application)/(user)/investment/(tabs)/_layout.tsx
Normal file
59
app/(application)/(user)/investment/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function InvestmentTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Bursa",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons
|
||||
name="bar-chart-outline"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="portofolio"
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Feather name="pie-chart" color={color} size={ICON_SIZE_SMALL} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="my-holding"
|
||||
options={{
|
||||
title: "Saham Saya",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="hand-holding-dollar"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="transaction"
|
||||
options={{
|
||||
title: "Transaksi",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="money-bill-transfer"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
81
app/(application)/(user)/investment/(tabs)/index.tsx
Normal file
81
app/(application)/(user)/investment/(tabs)/index.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
BaseBox,
|
||||
FloatingButton,
|
||||
Grid,
|
||||
ProgressCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import dayjs from "dayjs";
|
||||
import { Image } from "expo-image";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function InvestmentBursa() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/investment/create")} />
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingTop={7} paddingBottom={7} href={`/investment/${index}`}>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<Image
|
||||
source={DUMMY_IMAGE.background}
|
||||
style={{ width: "auto", height: 100, borderRadius: 10 }}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<StackCustom>
|
||||
<TextCustom truncate={2}>
|
||||
Title here : Lorem ipsum dolor sit amet consectetur
|
||||
adipisicing elit. Omnis, exercitationem, sequi enim quod
|
||||
distinctio maiores laudantium amet, quidem atque repellat sit
|
||||
vitae qui aliquam est veritatis laborum eum voluptatum totam!
|
||||
</TextCustom>
|
||||
<ProgressCustom value={index % 5 * 20} size="lg" />
|
||||
<TextCustom>
|
||||
Sisa waktu: {dayjs().diff(dayjs(), "day")} hari
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// <View style={{ padding: 20, gap: 16 }}>
|
||||
// <TextCustom>Progress 70%</TextCustom>
|
||||
// <ProgressCustom value={70} color="primary" size="md" />
|
||||
|
||||
// <TextCustom>Success Progress</TextCustom>
|
||||
// <ProgressCustom value={40} color="success" size="lg" />
|
||||
|
||||
// <TextCustom>Warning Progress (small)</TextCustom>
|
||||
// <ProgressCustom value={90} color="warning" size="sm" />
|
||||
|
||||
// <TextCustom>Error Indeterminate</TextCustom>
|
||||
// <ProgressCustom value={null} color="error" size="md" />
|
||||
|
||||
// <TextCustom>Custom Radius</TextCustom>
|
||||
// <ProgressCustom value={60} color="info" size="xl" radius={4} />
|
||||
|
||||
// <ProgressCustom value={70} color="primary" size="lg" />
|
||||
|
||||
// <ProgressCustom value={45} color="success" size="md" label="Halfway!" />
|
||||
|
||||
// <ProgressCustom value={90} color="warning" size="lg" showLabel={false} />
|
||||
|
||||
// <ProgressCustom value={null} color="error" size="sm" label="Loading..." />
|
||||
// </View>;
|
||||
49
app/(application)/(user)/investment/(tabs)/my-holding.tsx
Normal file
49
app/(application)/(user)/investment/(tabs)/my-holding.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
ProgressCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function InvestmentMyHolding() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingTop={7} paddingBottom={7}>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom truncate={2}>
|
||||
Title here : Lorem ipsum dolor sit amet consectetur
|
||||
adipisicing elit. Omnis, exercitationem, sequi enim quod
|
||||
distinctio maiores laudantium amet, quidem atque repellat sit
|
||||
vitae qui aliquam est veritatis laborum eum voluptatum totam!
|
||||
</TextCustom>
|
||||
|
||||
<Spacing height={5} />
|
||||
<TextCustom size="small">Rp. 7.500.000</TextCustom>
|
||||
<TextCustom size="small">300 Lembar</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={5}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ProgressCustom value={(index % 5) * 20} size="lg" />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
39
app/(application)/(user)/investment/(tabs)/portofolio.tsx
Normal file
39
app/(application)/(user)/investment/(tabs)/portofolio.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ScrollableCustom, ViewWrapper } from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import Investment_StatusBox from "@/screens/Invesment/StatusBox";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentPortofolio() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Investment_StatusBox
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
status={activeCategory as string}
|
||||
href={`/investment/${index}/${activeCategory}/detail`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
53
app/(application)/(user)/investment/(tabs)/transaction.tsx
Normal file
53
app/(application)/(user)/investment/(tabs)/transaction.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import dayjs from "dayjs";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function InvestmentTransaction() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<BaseBox key={i} paddingTop={7} paddingBottom={7}>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom truncate>
|
||||
Title Investment: Lorem ipsum dolor sit amet consectetur
|
||||
adipisicing elit. Am culpa excepturi deleniti soluta animi
|
||||
porro amet ducimus.
|
||||
</TextCustom>
|
||||
<TextCustom color="gray" size="small">
|
||||
{dayjs().format("DD/MM/YYYY")}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5} style={{ alignItems: "flex-end" }}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom bold truncate>
|
||||
Rp. 7.500.000
|
||||
</TextCustom>
|
||||
<BadgeCustom
|
||||
variant="light"
|
||||
color="success"
|
||||
style={GStyles.alignSelfFlexEnd}
|
||||
>
|
||||
Berhasil
|
||||
</BadgeCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function InvestmentAddDocument() {
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={() => router.back()}>Simpan</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="File dokumen bersifat opsional, jika memang ada file yang bisa membantu meyakinkan investor. Anda bisa mengupload nya." />
|
||||
<Spacing />
|
||||
<TextInputCustom
|
||||
label="Judul Dokumen"
|
||||
placeholder="Masukan judul dokumen"
|
||||
required
|
||||
/>
|
||||
|
||||
<BaseBox>
|
||||
<CenterCustom>
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() =>
|
||||
router.push("/(application)/(image)/take-picture/123")
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function InvestmentEditDocument() {
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="File dokumen bersifat opsional, jika memang ada file yang bisa membantu meyakinkan investor. Anda bisa mengupload nya." />
|
||||
<Spacing />
|
||||
<TextInputCustom
|
||||
label="Judul Dokumen"
|
||||
placeholder="Masukan judul dokumen"
|
||||
required
|
||||
/>
|
||||
|
||||
<BaseBox>
|
||||
<CenterCustom>
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() =>
|
||||
router.push("/(application)/(image)/take-picture/123")
|
||||
}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ViewWrapper } from "@/components";
|
||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
||||
|
||||
export default function InvestmentListOfDocument() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Investment_BoxDetailDocument
|
||||
key={index}
|
||||
title={`Judul Dokumen ${index + 1}`}
|
||||
href={`/investment/${index + 1}/dokumen/file`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
||||
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentRecapOfDocument() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openDrawerBox, setOpenDrawerBox] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Rekap Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setOpenDrawerBox(false);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Investment_BoxDetailDocument
|
||||
key={index}
|
||||
title={`Judul Dokumen ${index + 1}`}
|
||||
leftIcon={
|
||||
<Ionicons
|
||||
name="ellipsis-horizontal-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
style={{
|
||||
zIndex: 10,
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
onPress={() => setOpenDrawerBox(true)}
|
||||
/>
|
||||
}
|
||||
href={`/investment/${id}/dokumen/file`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer On Header */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: (
|
||||
<AntDesign
|
||||
name="pluscircle"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Tambah Dokumen",
|
||||
path: `/investment/${id}/(document)/add-document`,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
router.push(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Drawer On Box */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerBox}
|
||||
closeDrawer={() => setOpenDrawerBox(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit Dokumen",
|
||||
path: `/investment/${id}/(document)/edit-document`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<Ionicons
|
||||
name="trash-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Hapus Dokumen",
|
||||
path: "" as any,
|
||||
color: MainColor.red,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
if (item.path === ("" as any)) {
|
||||
AlertDefaultSystem({
|
||||
title: "Hapus Dokumen",
|
||||
message: "Apakah anda yakin ingin menghapus dokumen ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Hapus",
|
||||
onPressRight: () => {
|
||||
setOpenDrawerBox(false);
|
||||
},
|
||||
});
|
||||
}
|
||||
router.push(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
BaseBox,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
MenuDrawerDynamicGrid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentNews() {
|
||||
const { id, news } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Detail Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<DummyLandscapeImage />
|
||||
<TextCustom bold align="center" size="large">
|
||||
Judul Berita {news} Terbaru
|
||||
</TextCustom>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Laborum
|
||||
fuga mollitia laboriosam voluptatibus quos molestias, illo fugiat
|
||||
esse repellat, ad officia earum numquam? Aliquid corrupti quam
|
||||
tempora cum harum est!
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
label: "Hapus Berita",
|
||||
path: `/investment/${id}/add-news`,
|
||||
icon: <IconTrash />,
|
||||
color: "red",
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
AlertDefaultSystem({
|
||||
title: "Hapus Berita",
|
||||
message: "Apakah Anda yakin ingin menghapus berita ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Hapus",
|
||||
onPressRight: () => {
|
||||
router.back();
|
||||
setOpenDrawer(false);
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
53
app/(application)/(user)/investment/[id]/(news)/add-news.tsx
Normal file
53
app/(application)/(user)/investment/[id]/(news)/add-news.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function InvestmentAddNews() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Pengunggahan foto ke aplikasi bersifat opsional dan tidak diwajibkan, Anda dapat menyimpan berita tanpa mengunggah foto." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
<TextInputCustom
|
||||
label="Judul Berita"
|
||||
placeholder="Masukan judul berita"
|
||||
required
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Berita"
|
||||
placeholder="Masukan deskripsi berita"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
BackButton,
|
||||
BaseBox,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentListOfNews() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Daftar Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 15 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingBlock={5}
|
||||
href={`/investment/${id}/(news)/${index + 1}`}
|
||||
>
|
||||
<TextCustom bold>Berita Terbaru {index + 1}</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
label: "Tambah Berita",
|
||||
path: `/investment/${id}/add-news`,
|
||||
icon: <IconPlus />,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
router.push(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
BackButton,
|
||||
BaseBox,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentRecapOfNews() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Rekap Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 15 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingBlock={5}
|
||||
href={`/investment/${id}/(news)/${index + 1}`}
|
||||
>
|
||||
<TextCustom bold>Berita Terbaru {index + 1}</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
label: "Tambah Berita",
|
||||
path: `/investment/${id}/add-news`,
|
||||
icon: <IconPlus />,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
router.push(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
Divider,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function InvestmentInvest() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={() => router.push(`/investment/${id}/select-bank`)}>Beli</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||
<BaseBox>
|
||||
<StackCustom gap={"xs"}>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom>Sisa Lembar Saham</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>3.000</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom>Harga Per Lembar</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>Rp. 1.000</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Grid.Col
|
||||
span={6}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TextCustom>Jumlah Pembelian</TextCustom>
|
||||
<TextCustom bold color="yellow" size="small">
|
||||
Minimum 10 lembar
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={6}
|
||||
style={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<TextInputCustom
|
||||
style={{
|
||||
width: "80%",
|
||||
}}
|
||||
placeholder="0"
|
||||
keyboardType="numeric"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<Divider />
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom>Total Harga</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>Rp. 1.000</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
InformationBox,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
|
||||
export default function InvestmentInvoice() {
|
||||
const { id } = useLocalSearchParams();
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<StackCustom>
|
||||
<InformationBox text="Mohon transfer ke rekening dibawah" />
|
||||
<BaseBox>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom>Nama BANK</TextCustom>
|
||||
<TextCustom>Nama Penerima</TextCustom>
|
||||
<Spacing height={10} />
|
||||
|
||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
||||
<Grid.Col
|
||||
span={8}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TextCustom size="xlarge" bold color="yellow">
|
||||
4567898765433567
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<ButtonCustom>Salin</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
<BaseBox>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom>Jumlah Transaksi</TextCustom>
|
||||
|
||||
<Spacing height={10} />
|
||||
|
||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
||||
<Grid.Col
|
||||
span={8}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TextCustom size="xlarge" bold color="yellow">
|
||||
Rp. 1.000.000
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<ButtonCustom>Salin</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom>Upload bukti transfer anda.</TextCustom>
|
||||
<ButtonCenteredOnly onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123")
|
||||
}} icon="upload">
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
<ButtonCustom onPress={() => {
|
||||
router.push(`/investment/${id}/(transaction-flow)/process`)
|
||||
|
||||
}}>Saya Sudah Transfer</ButtonCustom>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
|
||||
|
||||
export default function InvestmentProcess() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<BaseBox>
|
||||
<TextCustom align="center">Menunggu Konfirmasi Admin</TextCustom>
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { BaseBox, BoxButtonOnFooter, ButtonCustom, ViewWrapper } from "@/components";
|
||||
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
||||
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentSelectBank() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [value, setValue] = useState<any | number>("");
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => router.push(`/investment/${id}/invoice`)}
|
||||
>
|
||||
Pilih
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||
<RadioGroup value={value} onChange={setValue}>
|
||||
{dummyMasterBank.map((item) => (
|
||||
<BaseBox key={item.name}>
|
||||
<RadioCustom label={item.name} value={item.code} />
|
||||
</BaseBox>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
20
app/(application)/(user)/investment/[id]/[file]/file.tsx
Normal file
20
app/(application)/(user)/investment/[id]/[file]/file.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { BackButton, TextCustom, ViewWrapper } from "@/components";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
|
||||
export default function InvestmentProspectus() {
|
||||
const { file } = useLocalSearchParams();
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Pratinjau ${_.startCase(file as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<TextCustom>Pratinjau File</TextCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
136
app/(application)/(user)/investment/[id]/[status]/detail.tsx
Normal file
136
app/(application)/(user)/investment/[id]/[status]/detail.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
||||
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
||||
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerDraft(false);
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
const bottomSection = (
|
||||
<Invesment_ComponentBoxOnBottomDetail
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
/>
|
||||
);
|
||||
|
||||
const buttonSection = (
|
||||
<Investment_ButtonInvestasiSection id={id as string} isMine={false} />
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<Invesment_DetailDataPublishSection
|
||||
status={status as string}
|
||||
bottomSection={bottomSection}
|
||||
buttonSection={buttonSection}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Draft Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerDraft}
|
||||
closeDrawer={() => setOpenDrawerDraft(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit Data",
|
||||
path: `/investment/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<AntDesign
|
||||
name="edit"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit Prospektus",
|
||||
path: `/investment/${id}/edit-prospectus`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<MaterialIcons
|
||||
name="create"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Update Dokumen",
|
||||
path: `/investment/${id}/(document)/recap-of-document`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePressDraft as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconDocument />,
|
||||
label: "Update Dokumen",
|
||||
path: `/investment/${id}/(document)/recap-of-document`,
|
||||
},
|
||||
{
|
||||
icon: <IconNews />,
|
||||
label: "Update Berita",
|
||||
path: `/investment/${id}/(news)/recap-of-news`,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
46
app/(application)/(user)/investment/[id]/edit-prospectus.tsx
Normal file
46
app/(application)/(user)/investment/[id]/edit-prospectus.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function InvestmentEditProspectus() {
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepan." />
|
||||
<Spacing />
|
||||
|
||||
<BaseBox>
|
||||
<CenterCustom>
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/(application)/(image)/take-picture/123")}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
164
app/(application)/(user)/investment/[id]/edit.tsx
Normal file
164
app/(application)/(user)/investment/[id]/edit.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import dummyPembagianDeviden from "@/lib/dummy-data/investment/pembagian-deviden";
|
||||
import dummyListPencarianInvestor from "@/lib/dummy-data/investment/pencarian-investor";
|
||||
import dummyPeriodeDeviden from "@/lib/dummy-data/investment/periode-deviden";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentEdit() {
|
||||
const [data, setData] = useState({
|
||||
title: "",
|
||||
targetDana: 0,
|
||||
hargaPerLembar: 0,
|
||||
totalLembar: 0,
|
||||
rasioKeuntungan: 0,
|
||||
pencarianInvestor: "",
|
||||
periodeDeviden: "",
|
||||
pembagianDeviden: "",
|
||||
});
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepannya." />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="Judul"
|
||||
label="Judul"
|
||||
value={data.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Target Dana"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Target Dana"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Harga Per Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="0"
|
||||
label="Total Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, totalLembar: Number(value) })
|
||||
}
|
||||
value={data.totalLembar === 0 ? "" : data.totalLembar.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconRight="%"
|
||||
label="Rasio Keuntungan / ROI %"
|
||||
placeholder="0"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, rasioKeuntungan: Number(value) })
|
||||
}
|
||||
value={
|
||||
data.rasioKeuntungan === 0 ? "" : data.rasioKeuntungan.toString()
|
||||
}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pencarian Investor"
|
||||
data={dummyListPencarianInvestor.map((item) => ({
|
||||
label: item.name + `${" "}hari`,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, pencarianInvestor: value as any })
|
||||
}
|
||||
value={data.pencarianInvestor}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Periode Deviden"
|
||||
data={dummyPeriodeDeviden.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, periodeDeviden: value as any })
|
||||
}
|
||||
value={data.periodeDeviden}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Pembagian Deviden"
|
||||
data={dummyPembagianDeviden.map((item) => ({
|
||||
label: item.name + `${" "}bulan`,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, pembagianDeviden: value as any })
|
||||
}
|
||||
value={data.pembagianDeviden}
|
||||
/>
|
||||
<Spacing />
|
||||
<ButtonCustom onPress={() => router.replace("/investment/portofolio")}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
134
app/(application)/(user)/investment/[id]/index.tsx
Normal file
134
app/(application)/(user)/investment/[id]/index.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
||||
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
||||
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentDetail() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerDraft(false);
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
const bottomSection = (
|
||||
<Invesment_ComponentBoxOnBottomDetail
|
||||
id={id as string}
|
||||
status={'publish'}
|
||||
/>
|
||||
);
|
||||
|
||||
const buttonSection = <Investment_ButtonInvestasiSection id={id as string} isMine={true} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<Invesment_DetailDataPublishSection
|
||||
status={"publish"}
|
||||
bottomSection={bottomSection}
|
||||
buttonSection={buttonSection}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Draft Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerDraft}
|
||||
closeDrawer={() => setOpenDrawerDraft(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit Data",
|
||||
path: `/investment/${id}/edit`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<AntDesign
|
||||
name="edit"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Edit Prospektus",
|
||||
path: `/investment/${id}/edit-prospectus`,
|
||||
},
|
||||
{
|
||||
icon: (
|
||||
<MaterialIcons
|
||||
name="create"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
),
|
||||
label: "Update Dokumen",
|
||||
path: `/investment/${id}/recap-of-document`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePressDraft as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconDocument />,
|
||||
label: "Update Dokumen",
|
||||
path: `/investment/${id}/recap-of-document`,
|
||||
},
|
||||
{
|
||||
icon: <IconNews />,
|
||||
label: "Update Berita",
|
||||
path: `/investment/${id}/(news)/recap-of-news`,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
21
app/(application)/(user)/investment/[id]/investor.tsx
Normal file
21
app/(application)/(user)/investment/[id]/investor.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
|
||||
export default function InvestmentInvestor() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BoxWithHeaderSection key={index}>
|
||||
<AvatarUsernameAndOtherComponent />
|
||||
<TextCustom bold>Rp. 7.000.000</TextCustom>
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
197
app/(application)/(user)/investment/create.tsx
Normal file
197
app/(application)/(user)/investment/create.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import dummyPembagianDeviden from "@/lib/dummy-data/investment/pembagian-deviden";
|
||||
import dummyListPencarianInvestor from "@/lib/dummy-data/investment/pencarian-investor";
|
||||
import dummyPeriodeDeviden from "@/lib/dummy-data/investment/periode-deviden";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function InvestmentCreate() {
|
||||
const [data, setData] = useState({
|
||||
title: "",
|
||||
targetDana: 0,
|
||||
hargaPerLembar: 0,
|
||||
totalLembar: 0,
|
||||
rasioKeuntungan: 0,
|
||||
pencarianInvestor: "",
|
||||
periodeDeviden: "",
|
||||
pembagianDeviden: "",
|
||||
});
|
||||
|
||||
// const [coba, setCoba] = useState("");
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
{/* <View style={GStyles.inputContainerInput}>
|
||||
<TextInput
|
||||
style={{
|
||||
...GStyles.inputText,
|
||||
}}
|
||||
onChangeText={(value) => setCoba(value)}
|
||||
value={coba}
|
||||
keyboardType="decimal-pad"
|
||||
/>
|
||||
</View> */}
|
||||
|
||||
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepannya." />
|
||||
|
||||
<BaseBox>
|
||||
<CenterCustom>
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
>
|
||||
Upload File
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="Judul"
|
||||
label="Judul"
|
||||
value={data.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Target Dana"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Target Dana"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconLeft="Rp."
|
||||
placeholder="0"
|
||||
label="Harga Per Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="0"
|
||||
label="Total Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, totalLembar: Number(value) })
|
||||
}
|
||||
value={data.totalLembar === 0 ? "" : data.totalLembar.toString()}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
iconRight="%"
|
||||
label="Rasio Keuntungan / ROI %"
|
||||
placeholder="0"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, rasioKeuntungan: Number(value) })
|
||||
}
|
||||
value={
|
||||
data.rasioKeuntungan === 0 ? "" : data.rasioKeuntungan.toString()
|
||||
}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pencarian Investor"
|
||||
data={dummyListPencarianInvestor.map((item) => ({
|
||||
label: item.name + `${" "}hari`,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, pencarianInvestor: value as any })
|
||||
}
|
||||
value={data.pencarianInvestor}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Periode Deviden"
|
||||
data={dummyPeriodeDeviden.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, periodeDeviden: value as any })
|
||||
}
|
||||
value={data.periodeDeviden}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Pembagian Deviden"
|
||||
data={dummyPembagianDeviden.map((item) => ({
|
||||
label: item.name + `${" "}bulan`,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, pembagianDeviden: value as any })
|
||||
}
|
||||
value={data.pembagianDeviden}
|
||||
/>
|
||||
<Spacing />
|
||||
<ButtonCustom onPress={() => router.replace("/investment/portofolio")}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="archive"
|
||||
options={{
|
||||
title: "Arsip",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="archive" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
16
app/(application)/(user)/job/(tabs)/archive.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
|
||||
export default function JobArchive() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox key={i} paddingTop={20} paddingBottom={20}>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
34
app/(application)/(user)/job/(tabs)/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/job/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari pekerjaan" />}
|
||||
>
|
||||
{jobDataDummy.map((item, index) => (
|
||||
<BoxWithHeaderSection key={index} onPress={() => router.push(`/job/${item.id}`)}>
|
||||
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${item.id}`} />
|
||||
<Spacing />
|
||||
<TextCustom truncate={2} align="center" bold size="large">
|
||||
{item.posisi}
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
50
app/(application)/(user)/job/(tabs)/status.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobStatus() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||
{jobDataDummy.map((e, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/job/${e.id}/${activeCategory}/detail`}
|
||||
// onPress={() => console.log("pressed")}
|
||||
>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e.posisi} {activeCategory?.toUpperCase()}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
65
app/(application)/(user)/job/[id]/[status]/detail.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function JobDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail} />
|
||||
<Job_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit",
|
||||
path: `/job/${id}/edit`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
67
app/(application)/(user)/job/[id]/edit.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobEdit() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
79
app/(application)/(user)/job/[id]/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { Alert, Linking } from "react-native";
|
||||
|
||||
export default function JobDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
|
||||
const OpenLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const openInBrowser = async () => {
|
||||
const supported = await Linking.canOpenURL(jobUrl);
|
||||
if (supported) {
|
||||
await Linking.openURL(jobUrl);
|
||||
} else {
|
||||
Alert.alert("Gagal membuka link", "Browser tidak tersedia.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={
|
||||
<Ionicons name="globe" size={ICON_SIZE_SMALL} color="white" />
|
||||
}
|
||||
onPress={openInBrowser}
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
>
|
||||
Buka Lowongan di Browser
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
const CopyLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await Clipboard.setStringAsync(jobUrl);
|
||||
Alert.alert(
|
||||
"Link disalin",
|
||||
"Tautan lowongan telah disalin ke clipboard."
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ButtonCustom
|
||||
iconLeft={<Ionicons name="copy" size={ICON_SIZE_SMALL} color="white" />}
|
||||
onPress={copyToClipboard}
|
||||
backgroundColor={MainColor.orange}
|
||||
textColor="white"
|
||||
>
|
||||
Salin Link
|
||||
</ButtonCustom>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail}/>
|
||||
<OpenLinkButton />
|
||||
<Spacing />
|
||||
<CopyLinkButton />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
73
app/(application)/(user)/job/create.tsx
Normal file
73
app/(application)/(user)/job/create.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function JobCreate() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/job/(tabs)/status")
|
||||
}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||
|
||||
<LandscapeFrameUploaded />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
43
app/(application)/(user)/voting/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
IconContribution,
|
||||
IconHistory,
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Tabs } from "expo-router";
|
||||
|
||||
export default function VotingTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
17
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
17
app/(application)/(user)/voting/(tabs)/contribution.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
|
||||
export default function VotingContribution() {
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection
|
||||
key={index}
|
||||
href={`/voting/${index}/contribution`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
37
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
37
app/(application)/(user)/voting/(tabs)/history.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ViewWrapper } from "@/components";
|
||||
import TabsTwoHeaderCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingHistory() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
headerComponent={
|
||||
<TabsTwoHeaderCustom
|
||||
leftValue="all"
|
||||
rightValue="main"
|
||||
leftText="Semua Riwayat"
|
||||
rightText="Riwayat Saya"
|
||||
activeCategory={activeCategory}
|
||||
handlePress={handlePress}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection
|
||||
key={index}
|
||||
id={activeCategory as any}
|
||||
href={`/voting/${index}/history`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
23
app/(application)/(user)/voting/(tabs)/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function VotingBeranda() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/voting/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari voting" />}
|
||||
>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection key={index} href={`/voting/${index}`} />
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
60
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
60
app/(application)/(user)/voting/(tabs)/status.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { masterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingStatus() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
<ScrollableCustom
|
||||
data={masterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/voting/${i}/${activeCategory}/detail`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
Lorem ipsum dolor sit {activeCategory}
|
||||
</TextCustom>
|
||||
<BadgeCustom
|
||||
style={{ width: "70%", alignSelf: "center" }}
|
||||
variant="light"
|
||||
>
|
||||
{dayjs().format("DD/MM/YYYY")} -{" "}
|
||||
{dayjs().add(1, "day").format("DD/MM/YYYY")}
|
||||
</BadgeCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
108
app/(application)/(user)/voting/[id]/[status]/detail.tsx
Normal file
108
app/(application)/(user)/voting/[id]/[status]/detail.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection";
|
||||
import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerDraft(false);
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
if (item.path === "") {
|
||||
AlertDefaultSystem({
|
||||
title: "Update Arsip",
|
||||
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
}
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail ${status}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailSection />
|
||||
<Voting_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Draft Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerDraft}
|
||||
closeDrawer={() => setOpenDrawerDraft(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconEdit />,
|
||||
label: "Edit",
|
||||
path: `/voting/${id}/edit`,
|
||||
},
|
||||
]}
|
||||
columns={4}
|
||||
onPressItem={handlePressDraft as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
{
|
||||
icon: <IconArchive />,
|
||||
label: "Update Arsip",
|
||||
path: "" as any,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
65
app/(application)/(user)/voting/[id]/contribution.tsx
Normal file
65
app/(application)/(user)/voting/[id]/contribution.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingDetailContribution() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Detail Kontribusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailContributionSection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
78
app/(application)/(user)/voting/[id]/edit.tsx
Normal file
78
app/(application)/(user)/voting/[id]/edit.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function VotingEdit() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.back()
|
||||
}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul Voting"
|
||||
placeholder="MasukanJudul Voting"
|
||||
required
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukan Deskripsi"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<DateTimePickerCustom label="Mulai Voting" required />
|
||||
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextInputCustom
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
64
app/(application)/(user)/voting/[id]/history.tsx
Normal file
64
app/(application)/(user)/voting/[id]/history.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import { Voting_BoxDetailHistorySection } from "@/screens/Voting/BoxDetailHistorySection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function VotingDetailHistory() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Riwayat Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailHistorySection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
87
app/(application)/(user)/voting/[id]/index.tsx
Normal file
87
app/(application)/(user)/voting/[id]/index.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
InformationBox,
|
||||
MenuDrawerDynamicGrid,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconArchive, IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
|
||||
export default function VotingDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
if (item.path === "") {
|
||||
AlertDefaultSystem({
|
||||
title: "Update Arsip",
|
||||
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
}
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail Voting`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<StackCustom>
|
||||
<InformationBox text="Untuk sementara voting ini belum di buka. Voting akan dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." />
|
||||
|
||||
<Voting_BoxDetailPublishSection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerPublish}
|
||||
closeDrawer={() => setOpenDrawerPublish(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
{
|
||||
icon: <IconArchive />,
|
||||
label: "Update Arsip",
|
||||
path: "" as any,
|
||||
},
|
||||
]}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
app/(application)/(user)/voting/[id]/list-of-contributor.tsx
Normal file
26
app/(application)/(user)/voting/[id]/list-of-contributor.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
|
||||
export default function Voting_ListOfContributor() {
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
rightComponent={
|
||||
<BadgeCustom
|
||||
style={{alignSelf: "flex-end" }}
|
||||
>
|
||||
Pilihan {index + 1}
|
||||
</BadgeCustom>
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
78
app/(application)/(user)/voting/create.tsx
Normal file
78
app/(application)/(user)/voting/create.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function VotingCreate() {
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/voting/(tabs)/status")
|
||||
}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul Voting"
|
||||
placeholder="MasukanJudul Voting"
|
||||
required
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukan Deskripsi"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<DateTimePickerCustom label="Mulai Voting" required />
|
||||
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextInputCustom
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React from "react";
|
||||
import {
|
||||
View,
|
||||
@@ -11,6 +12,8 @@ import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import EventDetailScreen from "./double-scroll";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import CustomUploadButton from "./upload-button";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
const { width } = Dimensions.get("window");
|
||||
|
||||
@@ -117,14 +120,38 @@ const CustomTabNavigator = () => {
|
||||
|
||||
const ActiveComponent = getActiveComponent();
|
||||
|
||||
const handleImageUpload = (file: any) => {
|
||||
console.log("Gambar dipilih:", file);
|
||||
// Upload ke server
|
||||
};
|
||||
|
||||
const handlePdfOrPngUpload = (file: any) => {
|
||||
console.log("PDF atau PNG dipilih:", file);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Custom Tab Navigator",
|
||||
}}
|
||||
/>
|
||||
<EventDetailScreen />
|
||||
<SafeAreaView edges={["bottom"]} style={styles.container}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Custom Tab Navigator",
|
||||
}}
|
||||
/>
|
||||
<EventDetailScreen />
|
||||
|
||||
<CustomUploadButton
|
||||
allowedExtensions={["jpeg", "png"]}
|
||||
buttonTitle="Unggah Gambar (JPEG/PNG)"
|
||||
onFileSelected={handleImageUpload}
|
||||
/>
|
||||
|
||||
{/* Hanya PDF atau PNG */}
|
||||
<CustomUploadButton
|
||||
allowedExtensions={["pdf"]}
|
||||
buttonTitle="Unggah PDF atau PNG"
|
||||
onFileSelected={handlePdfOrPngUpload}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
</>
|
||||
// <View style={styles.container}>
|
||||
// {/* Content Area */}
|
||||
|
||||
99
app/(application)/coba/upload-button.tsx
Normal file
99
app/(application)/coba/upload-button.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
// components/CustomUploadButton.tsx
|
||||
import React from 'react';
|
||||
import { Button, Alert, View, StyleSheet } from 'react-native';
|
||||
import * as DocumentPicker from 'expo-document-picker';
|
||||
import { isValidFileType, getMimeType } from '../../../utils/fileValidation';
|
||||
|
||||
interface UploadButtonProps {
|
||||
allowedExtensions: string[];
|
||||
buttonTitle?: string;
|
||||
onFileSelected?: (file: {
|
||||
uri: string;
|
||||
name: string;
|
||||
size: number | null;
|
||||
mimeType: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
const CustomUploadButton: React.FC<UploadButtonProps> = ({
|
||||
allowedExtensions,
|
||||
buttonTitle = 'Pilih File',
|
||||
onFileSelected,
|
||||
}) => {
|
||||
const handlePickFile = async () => {
|
||||
try {
|
||||
// Coba filter dengan MIME type jika memungkinkan
|
||||
const typeFilter = getMimeTypeFilter(allowedExtensions);
|
||||
|
||||
const result = await DocumentPicker.getDocumentAsync({
|
||||
type: typeFilter, // Ini membantu memfilter di UI pemilih
|
||||
copyToCacheDirectory: true,
|
||||
});
|
||||
|
||||
if (result.canceled) {
|
||||
Alert.alert('Dibatalkan', 'Tidak ada file yang dipilih.');
|
||||
return;
|
||||
}
|
||||
|
||||
const file = result.assets[0];
|
||||
const { uri, name, size } = file;
|
||||
|
||||
// Validasi ekstensi secara manual (cadangan jika MIME tidak akurat)
|
||||
if (!isValidFileType(name, allowedExtensions)) {
|
||||
Alert.alert(
|
||||
'Format Tidak Didukung',
|
||||
`Hanya file dengan ekstensi berikut yang diperbolehkan: ${allowedExtensions.join(', ')}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const mimeType = getMimeType(name);
|
||||
|
||||
// Kirim data file ke komponen induk
|
||||
if (onFileSelected) {
|
||||
onFileSelected({ uri, name, size: size || null, mimeType });
|
||||
}
|
||||
|
||||
Alert.alert('Berhasil', `File ${name} berhasil dipilih!`);
|
||||
} catch (error) {
|
||||
console.error('Error picking file:', error);
|
||||
Alert.alert('Error', 'Terjadi kesalahan saat memilih file.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button title={buttonTitle} onPress={handlePickFile} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
// Fungsi bantu untuk menghasilkan MIME type filter dari ekstensi
|
||||
const getMimeTypeFilter = (extensions: string[]): string => {
|
||||
const mimeTypes: string[] = [];
|
||||
extensions.forEach((ext) => {
|
||||
switch (ext.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
if (!mimeTypes.includes('image/jpeg')) mimeTypes.push('image/jpeg');
|
||||
break;
|
||||
case 'png':
|
||||
if (!mimeTypes.includes('image/png')) mimeTypes.push('image/png');
|
||||
break;
|
||||
case 'pdf':
|
||||
if (!mimeTypes.includes('application/pdf')) mimeTypes.push('application/pdf');
|
||||
break;
|
||||
default:
|
||||
mimeTypes.push('*/*'); // fallback
|
||||
}
|
||||
});
|
||||
return mimeTypes.length > 0 ? mimeTypes.join(',') : '*/*';
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default CustomUploadButton;
|
||||
BIN
assets/images/constants/crowd-hipmi.png
Normal file
BIN
assets/images/constants/crowd-hipmi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 331 KiB |
16
bun.lock
16
bun.lock
@@ -11,12 +11,16 @@
|
||||
"@react-navigation/elements": "^2.3.8",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
"expo-image": "~2.3.2",
|
||||
@@ -28,6 +32,7 @@
|
||||
"expo-symbols": "~0.4.5",
|
||||
"expo-system-ui": "~5.0.10",
|
||||
"expo-web-browser": "~14.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-native": "0.79.5",
|
||||
@@ -35,6 +40,7 @@
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
"react-native-maps": "1.20.1",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-pager-view": "6.7.1",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
@@ -447,6 +453,8 @@
|
||||
|
||||
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.3", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg=="],
|
||||
|
||||
"@types/react": ["@types/react@19.0.14", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-ixLZ7zG7j1fM0DijL9hDArwhwcCb4vqmePgwtV0GfnkHRSCUEv4LvzarcTdhoqgyMznUx/EhoTUv31CKZzkQlw=="],
|
||||
@@ -827,8 +835,12 @@
|
||||
|
||||
"expo-camera": ["expo-camera@16.1.10", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-qoRJeSwPmMbuu0VfnQTC+q79Kt2SqTWColEImgithL9u0qUQcC55U89IfhZk55Hpt6f1DgKuDzUOG5oY+snSWg=="],
|
||||
|
||||
"expo-clipboard": ["expo-clipboard@7.1.5", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-TCANUGOxouoJXxKBW5ASJl2WlmQLGpuZGemDCL2fO5ZMl57DGTypUmagb0CVUFxDl0yAtFIcESd78UsF9o64aw=="],
|
||||
|
||||
"expo-constants": ["expo-constants@17.1.7", "", { "dependencies": { "@expo/config": "~11.0.12", "@expo/env": "~1.0.7" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA=="],
|
||||
|
||||
"expo-document-picker": ["expo-document-picker@13.1.6", "", { "peerDependencies": { "expo": "*" } }, "sha512-8FTQPDOkyCvFN/i4xyqzH7ELW4AsB6B3XBZQjn1FEdqpozo6rpNJRr7sWFU/93WrLgA9FJEKpKbyr6XxczK6BA=="],
|
||||
|
||||
"expo-file-system": ["expo-file-system@18.1.11", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ=="],
|
||||
|
||||
"expo-font": ["expo-font@13.3.2", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A=="],
|
||||
@@ -1151,6 +1163,8 @@
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
@@ -1389,6 +1403,8 @@
|
||||
|
||||
"react-native-otp-entry": ["react-native-otp-entry@1.8.5", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-TZNkIuUzZKAAWrC8X/A22ZHJdycLysxUNysrGf0yTmDLRUyf4zLXwVFcDYUcRNe763Hjaf5qvtKGILb6lDGzoA=="],
|
||||
|
||||
"react-native-pager-view": ["react-native-pager-view@6.7.1", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-cBSr6xw4g5N7Kd3VGWcf+kmaH7iBWb0DXAf2bVo3bXkzBcBbTOmYSvc0LVLHhUPW8nEq5WjT9LCIYAzgF++EXw=="],
|
||||
|
||||
"react-native-paper": ["react-native-paper@5.14.5", "", { "dependencies": { "@callstack/react-theme-provider": "^3.0.9", "color": "^3.1.2", "use-latest-callback": "^0.2.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-safe-area-context": "*" } }, "sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ=="],
|
||||
|
||||
"react-native-reanimated": ["react-native-reanimated@3.17.5", "", { "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", "@babel/plugin-transform-class-properties": "^7.0.0-0", "@babel/plugin-transform-classes": "^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", "@babel/plugin-transform-optional-chaining": "^7.0.0-0", "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", "@babel/plugin-transform-template-literals": "^7.0.0-0", "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", "invariant": "^2.2.4", "react-native-is-edge-to-edge": "1.1.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0", "react": "*", "react-native": "*" } }, "sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw=="],
|
||||
|
||||
189
components/Badge/BadgeCustom.tsx
Normal file
189
components/Badge/BadgeCustom.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import React from "react";
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextStyle,
|
||||
View,
|
||||
ViewProps,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
|
||||
type BadgeVariant = "filled" | "light" | "outline" | "dot";
|
||||
type BadgeColor =
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "gray"
|
||||
| "dark";
|
||||
type BadgeSize = "xs" | "sm" | "md" | "lg";
|
||||
|
||||
interface BadgeProps extends ViewProps {
|
||||
children: React.ReactNode;
|
||||
variant?: BadgeVariant;
|
||||
color?: BadgeColor;
|
||||
size?: BadgeSize;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
radius?: number;
|
||||
fullWidth?: boolean;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
const BadgeCustom: React.FC<BadgeProps> = ({
|
||||
children,
|
||||
variant = "filled",
|
||||
color = "primary",
|
||||
size = "md",
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
radius = 50,
|
||||
fullWidth = false,
|
||||
textColor = "#fff",
|
||||
style,
|
||||
...props
|
||||
}) => {
|
||||
const colors = {
|
||||
primary: "#339AF0",
|
||||
success: "#40C057",
|
||||
warning: "#FAB005",
|
||||
danger: "#FA5252",
|
||||
gray: "#868E96",
|
||||
dark: "#212529",
|
||||
};
|
||||
|
||||
const themeColor = colors[color];
|
||||
|
||||
// Ganti bagian sizeStyles dan styles.container
|
||||
const sizeStyles = {
|
||||
xs: {
|
||||
fontSize: 10,
|
||||
paddingHorizontal: 6,
|
||||
paddingVertical: 2,
|
||||
height: 18, // Dinaikkan dari 16 → 18 agar teks tidak terpotong
|
||||
lineHeight: 10, // 👈 Penting: match fontSize agar kontrol vertikal lebih baik
|
||||
},
|
||||
sm: {
|
||||
fontSize: 11,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 3,
|
||||
height: 20,
|
||||
lineHeight: 11,
|
||||
},
|
||||
md: {
|
||||
fontSize: 12,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
height: 24,
|
||||
lineHeight: 12,
|
||||
},
|
||||
lg: {
|
||||
fontSize: 14,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
height: 30,
|
||||
lineHeight: 14,
|
||||
},
|
||||
};
|
||||
|
||||
const currentSize = sizeStyles[size];
|
||||
|
||||
let variantStyles: ViewStyle & { text: TextStyle } = {
|
||||
backgroundColor: themeColor,
|
||||
borderColor: themeColor,
|
||||
borderWidth: 1,
|
||||
borderRadius: radius,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
text: { color: textColor, fontWeight: "600" },
|
||||
};
|
||||
|
||||
switch (variant) {
|
||||
case "light":
|
||||
variantStyles.backgroundColor = `${themeColor}20`;
|
||||
variantStyles.text.color = themeColor;
|
||||
break;
|
||||
case "outline":
|
||||
variantStyles.backgroundColor = "transparent";
|
||||
variantStyles.text.color = themeColor;
|
||||
break;
|
||||
case "dot":
|
||||
variantStyles.backgroundColor = themeColor;
|
||||
variantStyles.paddingHorizontal = 0;
|
||||
variantStyles.paddingVertical = 0;
|
||||
variantStyles.height = currentSize.fontSize * 2;
|
||||
variantStyles.width = currentSize.fontSize * 2;
|
||||
variantStyles.borderRadius = currentSize.fontSize;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (variant === "dot") {
|
||||
return (
|
||||
<View
|
||||
style={[variantStyles, fullWidth && styles.fullWidth, style]}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
variantStyles,
|
||||
currentSize,
|
||||
{ borderRadius: radius },
|
||||
fullWidth && styles.fullWidth,
|
||||
style,
|
||||
]}
|
||||
{...props}
|
||||
>
|
||||
{leftIcon && <View style={styles.iconContainer}>{leftIcon}</View>}
|
||||
<Text
|
||||
style={[
|
||||
styles.text,
|
||||
variantStyles.text,
|
||||
{ fontSize: currentSize.fontSize },
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
{rightIcon && <View style={styles.iconContainer}>{rightIcon}</View>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignSelf: "flex-start",
|
||||
flexDirection: "row",
|
||||
alignItems: "center", // Vertikal center anak-anak (termasuk teks)
|
||||
justifyContent: "center", // Horizontal center
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
minWidth: 20,
|
||||
borderRadius: 6,
|
||||
// ❌ Jangan gunakan `height` fix di sini — kita override per size
|
||||
},
|
||||
text: {
|
||||
fontWeight: "600",
|
||||
textAlign: "center",
|
||||
// ❌ Hapus marginHorizontal jika mengganggu alignment
|
||||
// marginHorizontal: 2, // Opsional, bisa dihapus atau dikurangi
|
||||
includeFontPadding: false, // 👈 Ini penting untuk Android!
|
||||
padding: 0, // Bersihkan padding tambahan dari font
|
||||
},
|
||||
iconContainer: {
|
||||
marginHorizontal: 2, // Lebih kecil dari sebelumnya agar tidak ganggu ukuran kecil
|
||||
},
|
||||
fullWidth: {
|
||||
width: "100%",
|
||||
alignSelf: "stretch",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
|
||||
export default BadgeCustom;
|
||||
@@ -72,6 +72,8 @@ export default function BaseBox({
|
||||
marginBottom,
|
||||
paddingBlock,
|
||||
paddingInline,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
},
|
||||
style,
|
||||
]}
|
||||
|
||||
@@ -7,7 +7,7 @@ import BaseBox from "./BaseBox";
|
||||
export default function InformationBox({ text }: { text: string }) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<BaseBox paddingTop={5} paddingBottom={5}>
|
||||
<Grid>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
|
||||
44
components/Container/CircleContainer.tsx
Normal file
44
components/Container/CircleContainer.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import React from "react";
|
||||
import { StyleSheet, TextInput, View } from "react-native";
|
||||
|
||||
interface CircularInputProps {
|
||||
value: string | number;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange }) => {
|
||||
return (
|
||||
<View style={styles.circleContainer}>
|
||||
<TextInput
|
||||
value={String(value)}
|
||||
onChangeText={onChange}
|
||||
style={styles.input}
|
||||
keyboardType="numeric"
|
||||
maxLength={2} // Batasan maksimal karakter
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
circleContainer: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 40, // Setiap setengah dari lebar/tinggi
|
||||
borderWidth: 2,
|
||||
borderColor: MainColor.yellow, // Warna kuning
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
input: {
|
||||
color: MainColor.yellow, // Warna kuning
|
||||
fontSize: 24,
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
padding: 0,
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
});
|
||||
|
||||
export default CircularInput;
|
||||
@@ -90,7 +90,7 @@ const Col: React.FC<ColProps> = ({ children, span, style }) => {
|
||||
col: {
|
||||
flexBasis: `${(100 / columns) * colSpan}%`,
|
||||
paddingVertical: margin,
|
||||
// marginBottom: gap,
|
||||
// marginBottom: gap,
|
||||
marginBlock: gap,
|
||||
},
|
||||
});
|
||||
|
||||
193
components/Progress/ProgressCustom.tsx
Normal file
193
components/Progress/ProgressCustom.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import { useTheme } from "@react-navigation/native";
|
||||
import React from "react";
|
||||
import { Animated, StyleSheet, Text, View, ViewStyle } from "react-native";
|
||||
|
||||
type ProgressColor =
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "error"
|
||||
| "info"
|
||||
| "dark";
|
||||
|
||||
type ProgressSize = "xs" | "sm" | "md" | "lg" | "xl";
|
||||
|
||||
interface ProgressProps {
|
||||
value?: number | null;
|
||||
color?: ProgressColor;
|
||||
size?: ProgressSize;
|
||||
radius?: number;
|
||||
style?: ViewStyle;
|
||||
animated?: boolean;
|
||||
label?: React.ReactNode; // Konten label (bisa string, number, atau elemen)
|
||||
showLabel?: boolean; // Jika ingin mengontrol visibilitas
|
||||
}
|
||||
|
||||
const getColor = (color: ProgressColor, isDark: boolean) => {
|
||||
const palette: Record<ProgressColor, string> = {
|
||||
primary: "#FFC107",
|
||||
success: "#228B22",
|
||||
warning: "#FFA500",
|
||||
error: "#DC3545",
|
||||
info: "#177DDC",
|
||||
dark: isDark ? "#DADADA" : "#212121",
|
||||
};
|
||||
return palette[color];
|
||||
};
|
||||
|
||||
const getSize = (size: ProgressSize): number => {
|
||||
const sizes: Record<ProgressSize, number> = {
|
||||
xs: 6,
|
||||
sm: 8,
|
||||
md: 12,
|
||||
lg: 16,
|
||||
xl: 20,
|
||||
};
|
||||
return sizes[size];
|
||||
};
|
||||
|
||||
const ProgressCustom: React.FC<ProgressProps> = ({
|
||||
value = 0,
|
||||
color = "primary",
|
||||
size = "md",
|
||||
radius = 999,
|
||||
style,
|
||||
animated = true,
|
||||
label,
|
||||
showLabel = true,
|
||||
}) => {
|
||||
const { dark } = useTheme();
|
||||
const isDark = dark ?? false;
|
||||
|
||||
const barHeight = getSize(size);
|
||||
const progressColor = getColor(color, isDark);
|
||||
const displayValue =
|
||||
typeof value === "number" ? Math.max(0, Math.min(100, value)) : 0;
|
||||
|
||||
// Animasi indeterminate
|
||||
const translateX = React.useRef(new Animated.Value(-1)).current;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (value === null && animated) {
|
||||
const animation = Animated.loop(
|
||||
Animated.timing(translateX, {
|
||||
toValue: 1,
|
||||
duration: 1200,
|
||||
useNativeDriver: true,
|
||||
})
|
||||
);
|
||||
animation.start();
|
||||
return () => animation.stop();
|
||||
}
|
||||
}, [value, animated, translateX]);
|
||||
|
||||
const isIndeterminate = value === null;
|
||||
|
||||
// Tentukan teks label
|
||||
const labelText =
|
||||
label !== undefined
|
||||
? label
|
||||
: typeof value === "number"
|
||||
? `${Math.round(value)}%`
|
||||
: "";
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
height: barHeight,
|
||||
borderRadius: radius,
|
||||
backgroundColor: isDark
|
||||
? "rgb(255, 255, 255)"
|
||||
: "rgba(255, 255, 255, 0.84)",
|
||||
},
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{/* Progress Fill */}
|
||||
{isIndeterminate ? (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.indeterminateBar,
|
||||
{
|
||||
width: "50%",
|
||||
height: barHeight,
|
||||
borderRadius: radius,
|
||||
backgroundColor: progressColor,
|
||||
transform: [
|
||||
{
|
||||
translateX: translateX.interpolate({
|
||||
inputRange: [-1, 1],
|
||||
outputRange: [-100, 100],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
styles.progressBar,
|
||||
{
|
||||
width: `${displayValue}%`,
|
||||
height: barHeight,
|
||||
borderRadius: radius,
|
||||
backgroundColor: progressColor,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Label di tengah */}
|
||||
{showLabel && labelText ? (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<Text
|
||||
style={[
|
||||
styles.label,
|
||||
{
|
||||
fontSize: barHeight * 0.7,
|
||||
lineHeight: barHeight,
|
||||
color: isDark ? "black" : "black", // Warna teks, bisa disesuaikan
|
||||
fontWeight: "600",
|
||||
},
|
||||
]}
|
||||
numberOfLines={1}
|
||||
adjustsFontSizeToFit
|
||||
>
|
||||
{labelText}
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressCustom;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
overflow: "hidden",
|
||||
backgroundColor: "rgba(0,0,0,0.1)",
|
||||
width: "100%",
|
||||
justifyContent: "center", // Pusatkan label secara vertikal
|
||||
},
|
||||
progressBar: {
|
||||
backgroundColor: "#007BFF",
|
||||
},
|
||||
indeterminateBar: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
backgroundColor: "#007BFF",
|
||||
},
|
||||
label: {
|
||||
textAlign: "center",
|
||||
width: "100%",
|
||||
// Hindari overlap dengan background — bisa tambahkan shadow atau background jika perlu
|
||||
textShadowColor: "rgba(255,255,255,0.6)",
|
||||
textShadowOffset: { width: 1, height: 1 },
|
||||
textShadowRadius: 1,
|
||||
},
|
||||
});
|
||||
13
components/_Icon/IconArchive.tsx
Normal file
13
components/_Icon/IconArchive.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconArchive({ color }: { color?: string }) {
|
||||
return (
|
||||
<Ionicons
|
||||
name="archive"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
14
components/_Icon/IconContribution.tsx
Normal file
14
components/_Icon/IconContribution.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconContribution({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<Ionicons
|
||||
size={ICON_SIZE_SMALL}
|
||||
name="people"
|
||||
color={color || "white"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
25
components/_Icon/IconDocument.tsx
Normal file
25
components/_Icon/IconDocument.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { FontAwesome6, MaterialIcons } from "@expo/vector-icons";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
|
||||
export { IconDocument, IconDocumentEdit };
|
||||
|
||||
function IconDocument({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<FontAwesome6
|
||||
name="file-lines"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function IconDocumentEdit({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
name="edit-document"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
9
components/_Icon/IconHistory.tsx
Normal file
9
components/_Icon/IconHistory.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
|
||||
export default function IconHistory({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<FontAwesome5 size={20} name="history" color={color} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
components/_Icon/IconHome.tsx
Normal file
10
components/_Icon/IconHome.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconHome({ color }: { color?: string }) {
|
||||
return (
|
||||
<>
|
||||
<Ionicons name="home" size={ICON_SIZE_SMALL} color={color || "white"} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
15
components/_Icon/IconNews.tsx
Normal file
15
components/_Icon/IconNews.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
|
||||
export { IconNews };
|
||||
|
||||
function IconNews({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
name="newspaper"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
15
components/_Icon/IconPlus.tsx
Normal file
15
components/_Icon/IconPlus.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
|
||||
export { IconPlus };
|
||||
|
||||
function IconPlus({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<Octicons
|
||||
name="plus-circle"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
21
components/_Icon/IconProspectus.tsx
Normal file
21
components/_Icon/IconProspectus.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { FontAwesome6, MaterialIcons } from "@expo/vector-icons";
|
||||
|
||||
export { IconProspectus , IconProspectusEdit};
|
||||
|
||||
function IconProspectus({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<FontAwesome6 name="file-contract" size={size || ICON_SIZE_MEDIUM} color={color || MainColor.white} />
|
||||
);
|
||||
}
|
||||
|
||||
function IconProspectusEdit({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
name="edit-note"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
12
components/_Icon/IconStatus.tsx
Normal file
12
components/_Icon/IconStatus.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
|
||||
export default function IconStatus({ color }: { color?: string }) {
|
||||
return (
|
||||
<MaterialIcons
|
||||
size={ICON_SIZE_SMALL}
|
||||
name="checklist-rtl"
|
||||
color={color || "white"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
15
components/_Icon/IconTrash.tsx
Normal file
15
components/_Icon/IconTrash.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export { IconTrash };
|
||||
|
||||
function IconTrash({ color, size }: { color?: string; size?: number }) {
|
||||
return (
|
||||
<Ionicons
|
||||
name="trash"
|
||||
size={size || ICON_SIZE_MEDIUM}
|
||||
color={color || MainColor.white}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,29 @@
|
||||
import IconContribution from "./IconContribution";
|
||||
import IconEdit from "./IconEdit";
|
||||
import IconHistory from "./IconHistory";
|
||||
import IconHome from "./IconHome";
|
||||
import IconStatus from "./IconStatus";
|
||||
import IconArchive from "./IconArchive";
|
||||
import { IconProspectus, IconProspectusEdit } from "./IconProspectus";
|
||||
import { IconDocument, IconDocumentEdit } from "./IconDocument";
|
||||
import { IconNews } from "./IconNews";
|
||||
import { IconPlus } from "./IconPlus";
|
||||
|
||||
export { IconEdit };
|
||||
export {
|
||||
IconContribution,
|
||||
IconEdit,
|
||||
IconHistory,
|
||||
IconHome,
|
||||
IconStatus,
|
||||
IconArchive,
|
||||
// Prospectus
|
||||
IconProspectus,
|
||||
IconProspectusEdit,
|
||||
// Document
|
||||
IconDocument,
|
||||
IconDocumentEdit,
|
||||
// News
|
||||
IconNews,
|
||||
// Plus
|
||||
IconPlus,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ImageSourcePropType, View } from "react-native";
|
||||
import { ImageSourcePropType } from "react-native";
|
||||
import Divider from "../Divider/Divider";
|
||||
import Grid from "../Grid/GridCustom";
|
||||
import AvatarCustom from "../Image/AvatarCustom";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
import Divider from "../Divider/Divider"
|
||||
|
||||
const AvatarUsernameAndOtherComponent = ({
|
||||
avatarHref,
|
||||
@@ -19,30 +19,28 @@ const AvatarUsernameAndOtherComponent = ({
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<Grid containerStyle={{ zIndex: 10 }}>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom source={avatar} href={avatarHref as any} />
|
||||
</Grid.Col>
|
||||
<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={rightComponent ? 6 : 10}
|
||||
style={{ justifyContent: "center" }}
|
||||
span={4}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
<TextCustom truncate bold>
|
||||
{name || "Username"}
|
||||
</TextCustom>
|
||||
{rightComponent}
|
||||
</Grid.Col>
|
||||
{rightComponent && (
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
{rightComponent}
|
||||
</Grid.Col>
|
||||
)}
|
||||
</Grid>
|
||||
{withBottomLine && <Divider marginTop={0} />}
|
||||
<View>
|
||||
</View>
|
||||
)}
|
||||
</Grid>
|
||||
{withBottomLine && <Divider marginTop={0} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
32
components/_ShareComponent/DummyLandscapeImage.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image } from "expo-image";
|
||||
import { StyleSheet } from "react-native";
|
||||
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function DummyLandscapeImage() {
|
||||
return (
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/preview-image/1");
|
||||
}}
|
||||
>
|
||||
<Image source={DUMMY_IMAGE.background} style={styles.backgroundImage} />
|
||||
</ClickableCustom>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
backgroundImage: {
|
||||
width: "100%",
|
||||
height: 200, // Tinggi background sesuai kebutuhan
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: 6,
|
||||
overflow: "hidden",
|
||||
borderWidth: 1,
|
||||
borderColor: AccentColor.blue,
|
||||
backgroundColor: "white",
|
||||
},
|
||||
});
|
||||
39
components/_ShareComponent/SearchInput.tsx
Normal file
39
components/_ShareComponent/SearchInput.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import TextInputCustom from "../TextInput/TextInputCustom";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { StyleProp, ViewStyle, TextStyle } from "react-native";
|
||||
|
||||
interface SearchInputProps {
|
||||
placeholder?: string;
|
||||
onPress?: () => void;
|
||||
iconLeft?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
style?: StyleProp<TextStyle>;
|
||||
}
|
||||
export default function SearchInput({
|
||||
placeholder,
|
||||
onPress,
|
||||
iconLeft,
|
||||
iconRight,
|
||||
containerStyle = { marginBottom: 0 },
|
||||
style,
|
||||
...props
|
||||
}: SearchInputProps) {
|
||||
return (
|
||||
<TextInputCustom
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder={placeholder}
|
||||
borderRadius={50}
|
||||
containerStyle={containerStyle}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
61
components/_ShareComponent/TabsTwoHeaderCustom.tsx
Normal file
61
components/_ShareComponent/TabsTwoHeaderCustom.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { MainColor, AccentColor } from "@/constants/color-palet";
|
||||
import { View } from "react-native";
|
||||
import ButtonCustom from "../Button/ButtonCustom";
|
||||
import Spacing from "./Spacing";
|
||||
|
||||
export default function TabsTwoHeaderCustom ({
|
||||
leftValue,
|
||||
rightValue,
|
||||
leftText,
|
||||
rightText,
|
||||
activeCategory,
|
||||
handlePress,
|
||||
}: {
|
||||
leftValue: string;
|
||||
rightValue: string;
|
||||
leftText: string;
|
||||
rightText: string;
|
||||
activeCategory: string | null;
|
||||
handlePress: (item: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
padding: 5,
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 50,
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === leftValue ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === leftValue ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress(leftValue)}
|
||||
>
|
||||
{leftText}
|
||||
</ButtonCustom>
|
||||
<Spacing width={"2%"} />
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === rightValue ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === rightValue ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress(rightValue)}
|
||||
>
|
||||
{rightText}
|
||||
</ButtonCustom>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,10 @@ import ButtonCenteredOnly from "./Button/ButtonCenteredOnly";
|
||||
import ButtonCustom from "./Button/ButtonCustom";
|
||||
import DotButton from "./Button/DotButton";
|
||||
import FloatingButton from "./Button/FloatingButton";
|
||||
// Badge
|
||||
import BadgeCustom from "./Badge/BadgeCustom";
|
||||
// Container
|
||||
import CircleContainer from "./Container/CircleContainer";
|
||||
// Checkbox
|
||||
import CheckboxCustom from "./Checkbox/CheckboxCustom";
|
||||
import CheckboxGroup from "./Checkbox/CheckboxGroup";
|
||||
@@ -49,6 +53,10 @@ import AvatarUsernameAndOtherComponent from "./_ShareComponent/AvataraAndOtherHe
|
||||
import Spacing from "./_ShareComponent/Spacing";
|
||||
import TabBarBackground from "./_ShareComponent/TabBarBackground";
|
||||
import ViewWrapper from "./_ShareComponent/ViewWrapper";
|
||||
import SearchInput from "./_ShareComponent/SearchInput";
|
||||
import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
|
||||
// Progress
|
||||
import ProgressCustom from "./Progress/ProgressCustom";
|
||||
|
||||
export {
|
||||
AlertCustom,
|
||||
@@ -66,6 +74,8 @@ export {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
// Badge
|
||||
BadgeCustom,
|
||||
// Center
|
||||
CenterCustom,
|
||||
// Checkbox
|
||||
@@ -73,6 +83,8 @@ export {
|
||||
CheckboxGroup,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// Container
|
||||
CircleContainer,
|
||||
// Divider
|
||||
Divider,
|
||||
DividerCustom,
|
||||
@@ -86,11 +98,15 @@ export {
|
||||
// Map
|
||||
MapCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
// Progress
|
||||
ProgressCustom,
|
||||
// Scroll
|
||||
ScrollableCustom,
|
||||
// Select
|
||||
SelectCustom,
|
||||
// ShareComponent
|
||||
SearchInput,
|
||||
DummyLandscapeImage,
|
||||
Spacing,
|
||||
// Stack
|
||||
StackCustom,
|
||||
|
||||
22
lib/dummy-data/_master/bank.ts
Normal file
22
lib/dummy-data/_master/bank.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const dummyMasterBank = [
|
||||
{
|
||||
name: "Bank BCA",
|
||||
code: "BCA",
|
||||
},
|
||||
{
|
||||
name: "Bank BNI",
|
||||
code: "BNI",
|
||||
},
|
||||
{
|
||||
name: "Bank BRI",
|
||||
code: "BRI",
|
||||
},
|
||||
{
|
||||
name: "Bank Mandiri",
|
||||
code: "MANDIRI",
|
||||
},
|
||||
{
|
||||
name: "Bank Permata",
|
||||
code: "PERMATA",
|
||||
},
|
||||
]
|
||||
71
lib/dummy-data/investment/dummy-data-not-publish.ts
Normal file
71
lib/dummy-data/investment/dummy-data-not-publish.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
export {listDataNotPublishInvesment, listDataPublishInvesment};
|
||||
|
||||
const listDataNotPublishInvesment = [
|
||||
{
|
||||
label: "Target Dana",
|
||||
value: "Rp. 7.500.000",
|
||||
},
|
||||
{
|
||||
label: "Harga Per Lembar",
|
||||
value: "Rp. 2.400",
|
||||
},
|
||||
{
|
||||
label: "Return Of Investment (ROI)",
|
||||
value: "3 %",
|
||||
},
|
||||
{
|
||||
label: "Total Lembar",
|
||||
value: "1.200",
|
||||
},
|
||||
{
|
||||
label: "Jadwal Pembagian",
|
||||
value: "Rp. 2.880.000",
|
||||
},
|
||||
{
|
||||
label: "Pembagian Deviden",
|
||||
value: "Selamanya",
|
||||
},
|
||||
{
|
||||
label: "Pencarian Investor",
|
||||
value: "30 Hari",
|
||||
},
|
||||
];
|
||||
|
||||
const listDataPublishInvesment = [
|
||||
{
|
||||
label: "Investor",
|
||||
value: "10",
|
||||
},
|
||||
{
|
||||
label: "Target Dana",
|
||||
value: "Rp. 7.500.000",
|
||||
},
|
||||
{
|
||||
label: "Harga Per Lembar",
|
||||
value: "Rp. 2.400",
|
||||
},
|
||||
{
|
||||
label: "Return Of Investment (ROI)",
|
||||
value: "3 %",
|
||||
},
|
||||
{
|
||||
label: "Total Lembar",
|
||||
value: "1.200",
|
||||
},
|
||||
{
|
||||
label: "Sisa Lembar",
|
||||
value: "600",
|
||||
},
|
||||
{
|
||||
label: "Jadwal Pembagian",
|
||||
value: "Rp. 2.880.000",
|
||||
},
|
||||
{
|
||||
label: "Pembagian Deviden",
|
||||
value: "Selamanya",
|
||||
},
|
||||
{
|
||||
label: "Pencarian Investor",
|
||||
value: "30 Hari",
|
||||
},
|
||||
];
|
||||
20
lib/dummy-data/investment/pembagian-deviden.ts
Normal file
20
lib/dummy-data/investment/pembagian-deviden.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const dummyPembagianDeviden = [
|
||||
{
|
||||
id: "1",
|
||||
name: "3",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "6",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "9",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "12",
|
||||
},
|
||||
];
|
||||
|
||||
export default dummyPembagianDeviden;
|
||||
20
lib/dummy-data/investment/pencarian-investor.ts
Normal file
20
lib/dummy-data/investment/pencarian-investor.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const dummyListPencarianInvestor = [
|
||||
{
|
||||
id: "1",
|
||||
name: "30",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "60",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
name: "90",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
name: "120",
|
||||
},
|
||||
];
|
||||
|
||||
export default dummyListPencarianInvestor;
|
||||
12
lib/dummy-data/investment/periode-deviden.ts
Normal file
12
lib/dummy-data/investment/periode-deviden.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
const dummyPeriodeDeviden = [
|
||||
{
|
||||
id: "1",
|
||||
name: "Selamanya",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Satu tahun",
|
||||
},
|
||||
];
|
||||
|
||||
export default dummyPeriodeDeviden;
|
||||
65
package-lock.json
generated
65
package-lock.json
generated
@@ -9,16 +9,21 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.1.0",
|
||||
"@react-native-community/datetimepicker": "8.4.1",
|
||||
"@react-navigation/bottom-tabs": "^7.4.2",
|
||||
"@react-navigation/drawer": "^7.5.2",
|
||||
"@react-navigation/elements": "^2.3.8",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
"expo-image": "~2.3.2",
|
||||
@@ -37,6 +42,7 @@
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
"react-native-maps": "1.20.1",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-pager-view": "6.7.1",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
@@ -2801,6 +2807,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/datetimepicker": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.1.tgz",
|
||||
"integrity": "sha512-DrK+CUS5fZnz8dhzBezirkzQTcNDdaXer3oDLh0z4nc2tbdIdnzwvXCvi8IEOIvleoc9L95xS5tKUl0/Xv71Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"expo": ">=52.0.0",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-windows": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"expo": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native-windows": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/assets-registry": {
|
||||
"version": "0.79.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.5.tgz",
|
||||
@@ -5343,6 +5372,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
@@ -6331,6 +6366,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/expo-clipboard": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/expo-clipboard/-/expo-clipboard-7.1.5.tgz",
|
||||
"integrity": "sha512-TCANUGOxouoJXxKBW5ASJl2WlmQLGpuZGemDCL2fO5ZMl57DGTypUmagb0CVUFxDl0yAtFIcESd78UsF9o64aw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*",
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-constants": {
|
||||
"version": "17.1.7",
|
||||
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.7.tgz",
|
||||
@@ -6345,6 +6391,15 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-document-picker": {
|
||||
"version": "13.1.6",
|
||||
"resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-13.1.6.tgz",
|
||||
"integrity": "sha512-8FTQPDOkyCvFN/i4xyqzH7ELW4AsB6B3XBZQjn1FEdqpozo6rpNJRr7sWFU/93WrLgA9FJEKpKbyr6XxczK6BA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"expo": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expo-file-system": {
|
||||
"version": "18.1.11",
|
||||
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.1.11.tgz",
|
||||
@@ -10442,6 +10497,16 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-pager-view": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.7.1.tgz",
|
||||
"integrity": "sha512-cBSr6xw4g5N7Kd3VGWcf+kmaH7iBWb0DXAf2bVo3bXkzBcBbTOmYSvc0LVLHhUPW8nEq5WjT9LCIYAzgF++EXw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper": {
|
||||
"version": "5.14.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.14.5.tgz",
|
||||
|
||||
@@ -18,12 +18,16 @@
|
||||
"@react-navigation/elements": "^2.3.8",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"dayjs": "^1.11.13",
|
||||
"expo": "53.0.17",
|
||||
"expo-blur": "~14.1.5",
|
||||
"expo-camera": "~16.1.10",
|
||||
"expo-clipboard": "~7.1.5",
|
||||
"expo-constants": "~17.1.7",
|
||||
"expo-document-picker": "~13.1.6",
|
||||
"expo-file-system": "~18.1.11",
|
||||
"expo-font": "~13.3.2",
|
||||
"expo-haptics": "~14.1.4",
|
||||
"expo-image": "~2.3.2",
|
||||
@@ -35,6 +39,7 @@
|
||||
"expo-symbols": "~0.4.5",
|
||||
"expo-system-ui": "~5.0.10",
|
||||
"expo-web-browser": "~14.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-native": "0.79.5",
|
||||
@@ -42,6 +47,7 @@
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
"react-native-maps": "1.20.1",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-pager-view": "6.7.1",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "5.4.0",
|
||||
|
||||
@@ -32,12 +32,13 @@ export default function LoginView() {
|
||||
|
||||
// router.navigate("/verification");
|
||||
// router.navigate(`/(application)/(user)/profile/${id}`);
|
||||
router.navigate("/(application)/(user)/home");
|
||||
// router.navigate("/(application)/(user)/home");
|
||||
// router.navigate(`/(application)/profile/${id}/edit`);
|
||||
// router.navigate(`/(application)/(user)/portofolio/${id}`)
|
||||
// router.navigate(`/(application)/(image)/preview-image/${id}`);
|
||||
// router.replace("/(application)/(user)/event/(tabs)");
|
||||
// router.replace("/(application)/coba");
|
||||
router.navigate("/investment/(tabs)")
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { BaseBox, ButtonCustom, StackCustom, TextCustom } from "@/components";
|
||||
import { BaseBox, 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";
|
||||
|
||||
|
||||
@@ -1,52 +1,55 @@
|
||||
import { TextCustom } from "@/components";
|
||||
import { ClickableCustom, TextCustom } from "@/components";
|
||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/FontAwesome";
|
||||
import { stylesHome } from "./homeViewStyle";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Home_BottomFeatureSection() {
|
||||
return (
|
||||
<>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10}/>
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
<ClickableCustom onPress={() => router.push("/job")}>
|
||||
<View style={stylesHome.jobVacancyContainer}>
|
||||
<View style={stylesHome.jobVacancyHeader}>
|
||||
<Icon name="briefcase" size={24} color="white" />
|
||||
<Spacing width={10} />
|
||||
<TextCustom bold size="large">
|
||||
Job Vacancy
|
||||
</TextCustom>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
<View style={stylesHome.vacancyList}>
|
||||
{/* Vacancy Item 1 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
Bagas_banuna
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Dicari perawat kucing dan perawat anjing
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Vacancy Item 2 */}
|
||||
<View style={stylesHome.vacancyItem}>
|
||||
{/* <Icon name="user" size={20} color="#FFD700" /> */}
|
||||
<View style={stylesHome.vacancyDetails}>
|
||||
<TextCustom bold color="yellow" truncate size="large">
|
||||
fibramarcell
|
||||
</TextCustom>
|
||||
<Spacing height={5} />
|
||||
<TextCustom truncate={2}>
|
||||
Di Butuhkan Seorang Programer dan Designer
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ClickableCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,42 +16,28 @@ export default function Home_FeatureSection() {
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/(user)/collaboration/(tabs)")}
|
||||
onPress={() =>
|
||||
router.push("/(application)/(user)/collaboration/(tabs)")
|
||||
}
|
||||
>
|
||||
<Ionicons name="share" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/(user)/voting/(tabs)")}
|
||||
>
|
||||
<Ionicons name="cube" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Voting</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/(user)/crowdfunding")}
|
||||
>
|
||||
<Ionicons name="heart" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Crowdfunding</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* <View style={stylesHome.gridContainer}>
|
||||
<TouchableOpacity
|
||||
style={stylesHome.gridItem}
|
||||
onPress={() => router.push("/(application)/event")}
|
||||
>
|
||||
<Ionicons name="analytics" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Event</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<Ionicons name="share" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Collaboration</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<Ionicons name="cube" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Voting</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={stylesHome.gridItem}>
|
||||
<Ionicons name="heart" size={48} color="white" />
|
||||
<Text style={stylesHome.gridLabel}>Crowdfunding</Text>
|
||||
</TouchableOpacity>
|
||||
</View> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
50
screens/Invesment/BoxDetailDataSection.tsx
Normal file
50
screens/Invesment/BoxDetailDataSection.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
BaseBox,
|
||||
DummyLandscapeImage,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Invesment_BoxDetailDataSection({
|
||||
title,
|
||||
data,
|
||||
bottomSection,
|
||||
}: {
|
||||
title?: string;
|
||||
data: any;
|
||||
bottomSection?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox paddingBottom={0}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<DummyLandscapeImage />
|
||||
<Spacing />
|
||||
<TextCustom align="center" size="xlarge" bold>
|
||||
{title || "Judul Investasi"}
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
|
||||
{data.map((item: any, index: any) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.label}</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={7} style={{ justifyContent: "center" }}>
|
||||
<TextCustom>{item.value}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
))}
|
||||
<Spacing />
|
||||
{bottomSection}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
16
screens/Invesment/BoxProgressSection.tsx
Normal file
16
screens/Invesment/BoxProgressSection.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { BaseBox, StackCustom, TextCustom, ProgressCustom } from "@/components";
|
||||
|
||||
export default function Invesment_BoxProgressSection({status}: {status: string}) {
|
||||
return (
|
||||
<>
|
||||
{status === "publish" && (
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Progress Saham</TextCustom>
|
||||
<ProgressCustom value={70} size="lg" />
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
screens/Invesment/ButtonInvestasiSection.tsx
Normal file
26
screens/Invesment/ButtonInvestasiSection.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ButtonCustom } from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Investment_ButtonInvestasiSection({
|
||||
id,
|
||||
isMine,
|
||||
}: {
|
||||
id: string;
|
||||
isMine: boolean;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{isMine ? (
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
router.navigate(`/investment/${id}/(transaction-flow)`);
|
||||
}}
|
||||
>
|
||||
Beli Saham
|
||||
</ButtonCustom>
|
||||
) : (
|
||||
<ButtonCustom disabled>Investasi Ini Milik Anda</ButtonCustom>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
125
screens/Invesment/ButtonStatusSection.tsx
Normal file
125
screens/Invesment/ButtonStatusSection.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function Investment_ButtonStatusSection({
|
||||
status,
|
||||
buttonPublish
|
||||
}: {
|
||||
status: string;
|
||||
buttonPublish?: React.ReactNode;
|
||||
}) {
|
||||
const handleBatalkanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Batalkan Review",
|
||||
message: "Apakah Anda yakin ingin batalkan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleAjukanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Ajukan Review",
|
||||
message: "Apakah Anda yakin ingin ajukan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditKembali = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Edit Kembali",
|
||||
message: "Apakah Anda yakin ingin edit kembali ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenDeleteAlert = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Hapus",
|
||||
message: "Apakah Anda yakin ingin menghapus data ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Hapus",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const DeleteButton = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
backgroundColor="red"
|
||||
textColor="white"
|
||||
onPress={handleOpenDeleteAlert}
|
||||
>
|
||||
Hapus
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case "publish":
|
||||
return <>
|
||||
{buttonPublish}
|
||||
</>;
|
||||
|
||||
case "review":
|
||||
return (
|
||||
<ButtonCustom onPress={handleBatalkanReview}>
|
||||
Batalkan Review
|
||||
</ButtonCustom>
|
||||
);
|
||||
|
||||
case "draft":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom onPress={handleAjukanReview}>
|
||||
Ajukan Review
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
{DeleteButton()}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
case "reject":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom onPress={handleEditKembali}>
|
||||
Edit Kembali
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
{DeleteButton()}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return <ButtonCustom disabled>Status Undifined</ButtonCustom>;
|
||||
}
|
||||
}
|
||||
126
screens/Invesment/ComponentBoxOnBottomDetail.tsx
Normal file
126
screens/Invesment/ComponentBoxOnBottomDetail.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import {
|
||||
BaseBox,
|
||||
CenterCustom,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import { IconDocument, IconNews, IconProspectus } from "@/components/_Icon";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function Invesment_ComponentBoxOnBottomDetail({
|
||||
id,
|
||||
status,
|
||||
}: {
|
||||
id: string;
|
||||
status: string;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{status === "publish" ? (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingInline: 5, marginBlock: 0 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/prospektus/file`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Prospektus</TextCustom>
|
||||
<CenterCustom>
|
||||
<IconProspectus size={50} color={MainColor.white} />
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6} style={{ paddingInline: 5, marginBlock: 0 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/(document)/list-of-document`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Dokumen</TextCustom>
|
||||
<CenterCustom>
|
||||
<IconDocument size={50} color={MainColor.white} />
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingInline: 5, marginBlock: 0 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/investor`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Investor</TextCustom>
|
||||
<CenterCustom>
|
||||
<Ionicons
|
||||
name="cash-outline"
|
||||
size={50}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6} style={{ paddingInline: 5, marginBlock: 0 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/list-of-news`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Berita</TextCustom>
|
||||
<CenterCustom>
|
||||
<IconNews size={50} color={MainColor.white} />
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/prospektus/file`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Prospektus</TextCustom>
|
||||
<CenterCustom>
|
||||
<IconProspectus size={50} color={MainColor.white} />
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
<BaseBox
|
||||
backgroundColor={AccentColor.blue}
|
||||
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
|
||||
href={`/investment/${id}/(document)/list-of-document`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center">Dokumen</TextCustom>
|
||||
<CenterCustom>
|
||||
<IconDocument size={50} color={MainColor.white} />
|
||||
</CenterCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
40
screens/Invesment/DetailDataPublishSection.tsx
Normal file
40
screens/Invesment/DetailDataPublishSection.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Spacing, StackCustom } from "@/components";
|
||||
import {
|
||||
listDataNotPublishInvesment,
|
||||
listDataPublishInvesment,
|
||||
} from "@/lib/dummy-data/investment/dummy-data-not-publish";
|
||||
import React from "react";
|
||||
import Invesment_BoxDetailDataSection from "./BoxDetailDataSection";
|
||||
import Invesment_BoxProgressSection from "./BoxProgressSection";
|
||||
import Investment_ButtonStatusSection from "./ButtonStatusSection";
|
||||
|
||||
export default function Invesment_DetailDataPublishSection({
|
||||
status,
|
||||
bottomSection,
|
||||
buttonSection,
|
||||
}: {
|
||||
status: string;
|
||||
bottomSection: React.ReactNode;
|
||||
buttonSection: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<StackCustom gap={"sm"}>
|
||||
<Invesment_BoxProgressSection status={status as string} />
|
||||
<Invesment_BoxDetailDataSection
|
||||
data={
|
||||
status === "publish"
|
||||
? listDataPublishInvesment
|
||||
: listDataNotPublishInvesment
|
||||
}
|
||||
bottomSection={bottomSection}
|
||||
/>
|
||||
<Investment_ButtonStatusSection
|
||||
status={status as string}
|
||||
buttonPublish={buttonSection}
|
||||
/>
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
}
|
||||
28
screens/Invesment/Document/RecapBoxDetail.tsx
Normal file
28
screens/Invesment/Document/RecapBoxDetail.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { BaseBox, Grid, TextCustom } from "@/components";
|
||||
import { Href } from "expo-router";
|
||||
|
||||
export default function Investment_BoxDetailDocument({
|
||||
title,
|
||||
leftIcon,
|
||||
href,
|
||||
}: {
|
||||
title: string;
|
||||
leftIcon?: React.ReactNode;
|
||||
href?: Href;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox href={href}>
|
||||
<Grid>
|
||||
<Grid.Col span={leftIcon ? 10 : 12}>
|
||||
<TextCustom truncate>
|
||||
{title ||
|
||||
`Judul Dokumen: Lorem, ipsum dolor sit amet consectetur adipisicing elit.`}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
{leftIcon && <Grid.Col span={2}>{leftIcon}</Grid.Col>}
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
48
screens/Invesment/StatusBox.tsx
Normal file
48
screens/Invesment/StatusBox.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { BaseBox, Grid, Spacing, TextCustom } from "@/components";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image } from "expo-image";
|
||||
import { Href } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface Investment_StatusBoxProps {
|
||||
id: string;
|
||||
status: string;
|
||||
href?: Href
|
||||
}
|
||||
|
||||
export default function Investment_StatusBox({
|
||||
id,
|
||||
status,
|
||||
href
|
||||
}: Investment_StatusBoxProps) {
|
||||
return (
|
||||
<BaseBox paddingTop={7} paddingBottom={7} href={href}>
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom truncate={2}>
|
||||
Title here : {status} Lorem ipsum dolor sit amet consectetur
|
||||
adipisicing elit. Omnis, exercitationem, sequi enim quod distinctio
|
||||
maiores laudantium amet, quidem atque repellat sit vitae qui aliquam
|
||||
est veritatis laborum eum voluptatum totam!
|
||||
</TextCustom>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextCustom bold size="small">
|
||||
Target Dana:
|
||||
</TextCustom>
|
||||
<TextCustom>Rp. 7.500.000</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>
|
||||
<Image
|
||||
source={DUMMY_IMAGE.background}
|
||||
style={{ width: "auto", height: 100, borderRadius: 10 }}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
);
|
||||
}
|
||||
28
screens/Job/BoxDetailSection.tsx
Normal file
28
screens/Job/BoxDetailSection.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { BaseBox, StackCustom, DummyLandscapeImage, TextCustom } from "@/components";
|
||||
|
||||
export default function Job_BoxDetailSection({data}: {data: any}) {
|
||||
return (
|
||||
<>
|
||||
<BaseBox>
|
||||
<StackCustom gap={"lg"}>
|
||||
<DummyLandscapeImage />
|
||||
|
||||
<TextCustom align="center" bold size="large">
|
||||
{data?.posisi}
|
||||
</TextCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Syarat & Ketentuan :</TextCustom>
|
||||
<TextCustom>{data?.syaratKetentuan}</TextCustom>
|
||||
</StackCustom>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>Deskripsi :</TextCustom>
|
||||
<TextCustom>{data?.deskripsi}</TextCustom>
|
||||
</StackCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
135
screens/Job/ButtonStatusSection.tsx
Normal file
135
screens/Job/ButtonStatusSection.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Job_ButtonStatusSection({
|
||||
status,
|
||||
}: {
|
||||
status: string;
|
||||
}) {
|
||||
const handleBatalkanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Batalkan Review",
|
||||
message: "Apakah Anda yakin ingin batalkan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleAjukanReview = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Ajukan Review",
|
||||
message: "Apakah Anda yakin ingin ajukan review ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditKembali = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Edit Kembali",
|
||||
message: "Apakah Anda yakin ingin edit kembali ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenDeleteAlert = () => {
|
||||
AlertDefaultSystem({
|
||||
title: "Hapus",
|
||||
message: "Apakah Anda yakin ingin menghapus data ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Hapus",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const DeleteButton = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
backgroundColor="red"
|
||||
textColor="white"
|
||||
onPress={handleOpenDeleteAlert}
|
||||
>
|
||||
Hapus
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
case "publish":
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Arsipkan");
|
||||
router.replace("/(application)/(user)/job/(tabs)/archive");
|
||||
}}
|
||||
>
|
||||
Arsipkan
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
|
||||
case "review":
|
||||
return (
|
||||
<ButtonCustom onPress={handleBatalkanReview}>
|
||||
Batalkan Review
|
||||
</ButtonCustom>
|
||||
);
|
||||
|
||||
case "draft":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleAjukanReview}>
|
||||
Ajukan Review
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
case "reject":
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Col span={5}>
|
||||
<ButtonCustom onPress={handleEditKembali}>
|
||||
Edit Kembali
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={5}>{DeleteButton()}</Grid.Col>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return <ButtonCustom disabled>Status Undifined</ButtonCustom>;
|
||||
}
|
||||
}
|
||||
82
screens/Job/listDataDummy.ts
Normal file
82
screens/Job/listDataDummy.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
export const jobDataDummy = [
|
||||
{
|
||||
id: 1,
|
||||
posisi: "Front-End Developer",
|
||||
deskripsi:
|
||||
"Kami mencari Front-End Developer yang berpengalaman untuk membangun dan mengembangkan antarmuka pengguna web yang interaktif, responsif, dan menarik menggunakan React.js dan TypeScript. Kandidat akan bekerja sama dengan tim desain dan back-end untuk memastikan pengalaman pengguna yang optimal di berbagai perangkat.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Front-End Developer. Menguasai React.js, TypeScript, HTML, CSS (SCSS). Memahami prinsip-prinsip desain responsif dan pengalaman pengguna (UX). Pengalaman dengan Git dan CI/CD pipeline menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
posisi: "Back-End Developer",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab dalam membangun dan memelihara RESTful API, serta mengelola logika server dan interaksi dengan basis data. Anda akan bekerja dengan Node.js dan Express untuk mendukung berbagai kebutuhan aplikasi kami yang dinamis dan real-time.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman minimal 2 tahun sebagai Back-End Developer. Menguasai Node.js, Express.js, dan basis data (MongoDB, PostgreSQL). Paham prinsip REST API, middleware, dan keamanan data. Pengalaman dengan Docker dan testing (Jest/Mocha) menjadi nilai plus.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
posisi: "UI/UX Designer",
|
||||
deskripsi:
|
||||
"Kami mencari desainer kreatif untuk merancang pengalaman pengguna dan antarmuka yang modern dan intuitif. Anda akan terlibat dalam proses riset pengguna, wireframing, prototyping, dan pengujian desain untuk aplikasi web dan mobile.",
|
||||
syaratKetentuan:
|
||||
"Minimal 1 tahun pengalaman dalam UI/UX. Menguasai alat desain seperti Figma, Sketch, atau Adobe XD. Mampu membuat prototipe interaktif dan melakukan usability testing. Portofolio desain UI/UX yang pernah dikerjakan wajib dilampirkan.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
posisi: "DevOps Engineer",
|
||||
deskripsi:
|
||||
"Sebagai DevOps Engineer, Anda akan bertanggung jawab atas deployment otomatis, pemantauan sistem, dan infrastruktur cloud kami. Posisi ini akan fokus pada integrasi dan pengiriman berkelanjutan (CI/CD), skalabilitas, serta keamanan sistem.",
|
||||
syaratKetentuan:
|
||||
"Menguasai Docker, Kubernetes, Jenkins, GitLab CI, dan monitoring tools (Prometheus, Grafana). Memiliki pengalaman kerja dengan platform cloud seperti AWS atau GCP. Familiar dengan scripting Bash/Python dan prinsip keamanan DevOps.",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
posisi: "Mobile Developer",
|
||||
deskripsi:
|
||||
"Kami sedang mencari developer mobile yang terampil menggunakan Flutter untuk mengembangkan aplikasi lintas platform (iOS & Android) yang efisien dan user-friendly. Anda akan bekerja dalam tim produk untuk merancang dan mengimplementasikan fitur baru.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman mengembangkan aplikasi Flutter setidaknya 1 tahun. Memahami state management (Provider, BLoC, atau Riverpod). Pernah merilis aplikasi di Google Play Store atau App Store. Pengalaman dengan Firebase menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
posisi: "Data Analyst",
|
||||
deskripsi:
|
||||
"Sebagai Data Analyst, Anda akan mengumpulkan, menganalisis, dan menginterpretasikan data untuk mendukung keputusan bisnis. Anda akan membuat dashboard interaktif, laporan rutin, dan memberikan insight yang bisa ditindaklanjuti kepada tim manajemen.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman menggunakan SQL, Excel, dan tools visualisasi data seperti Power BI atau Tableau. Kemampuan analisis statistik dan storytelling data. Pengalaman dengan Python (pandas, numpy) merupakan keunggulan.",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
posisi: "Machine Learning Engineer",
|
||||
deskripsi:
|
||||
"Kami membuka posisi untuk insinyur Machine Learning yang akan mengembangkan dan mengimplementasikan model prediktif untuk mendukung berbagai kebutuhan bisnis. Anda akan bekerja dengan data dalam skala besar dan menerapkan teknik NLP, klasifikasi, dan rekomendasi.",
|
||||
syaratKetentuan:
|
||||
"Memiliki pengalaman minimal 1 tahun dalam ML project. Menguasai Python, scikit-learn, TensorFlow, atau PyTorch. Mampu memproses data besar dengan pandas, NumPy, dan Spark. Familiar dengan deployment model ML ke production.",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
posisi: "System Administrator",
|
||||
deskripsi:
|
||||
"Posisi ini bertanggung jawab atas pengelolaan dan pemeliharaan sistem server Linux, jaringan internal, dan keamanan TI perusahaan. Termasuk setup server, monitoring, dan troubleshooting sistem operasional harian.",
|
||||
syaratKetentuan:
|
||||
"Berpengalaman mengelola server Linux (Ubuntu/CentOS), firewall, dan konfigurasi jaringan. Paham konsep load balancing, backup otomatis, dan disaster recovery. Kemampuan scripting (Bash, Python) menjadi nilai tambah.",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
posisi: "Quality Assurance",
|
||||
deskripsi:
|
||||
"Kami mencari QA Engineer untuk memastikan kualitas produk melalui pengujian manual maupun otomatis. Anda akan menulis test case, menjalankan regresi test, dan bekerja erat dengan tim developer untuk menemukan serta memperbaiki bug lebih awal.",
|
||||
syaratKetentuan:
|
||||
"Pengalaman dalam manual testing dan tools otomasi seperti Selenium, Cypress, atau Playwright. Memahami prinsip SDLC dan metodologi Agile. Mampu menulis dokumentasi QA dan membuat test report secara sistematis.",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
posisi: "Product Manager",
|
||||
deskripsi:
|
||||
"Sebagai Product Manager, Anda akan memimpin pengembangan produk mulai dari perencanaan, peluncuran, hingga peningkatan. Anda akan mengelola roadmap, prioritas fitur, dan bekerja lintas fungsi dengan tim desain, dev, dan bisnis.",
|
||||
syaratKetentuan:
|
||||
"Minimal 2 tahun pengalaman sebagai Product Manager. Memahami Agile/Scrum. Keterampilan komunikasi dan analisis yang kuat. Pengalaman dalam tools seperti Jira, Confluence, dan alat wireframing. Kemampuan mengambil keputusan berbasis data.",
|
||||
},
|
||||
];
|
||||
35
screens/Voting/BoxDetailContribution.tsx
Normal file
35
screens/Voting/BoxDetailContribution.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
BoxWithHeaderSection,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection";
|
||||
|
||||
export function Voting_BoxDetailContributionSection({
|
||||
headerAvatar,
|
||||
}: {
|
||||
headerAvatar?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BoxWithHeaderSection>
|
||||
{headerAvatar ? headerAvatar : <Spacing />}
|
||||
<StackCustom gap={"lg"}>
|
||||
<Voting_ComponentDetailDataSection />
|
||||
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom bold size="small" align="center">
|
||||
Pilihan Anda
|
||||
</TextCustom>
|
||||
<BadgeCustom style={[GStyles.alignSelfCenter]}>
|
||||
Pilihan 1
|
||||
</BadgeCustom>
|
||||
</StackCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user