Compare commits
28 Commits
api-file/2
...
api-invest
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f05d1f7f0 | |||
| 3d8d8568a3 | |||
| ccdd7730b2 | |||
| a474aebb94 | |||
| 29b65aeebf | |||
| 18beb09b42 | |||
| 54af104f8a | |||
| 8c5602b809 | |||
| 99f058a92f | |||
| 821a211f58 | |||
| 333b1d2512 | |||
| 391430de46 | |||
| ce79d7c240 | |||
| d09a566903 | |||
| 60b0befa60 | |||
| 3287f4c287 | |||
| 76fb14ed0c | |||
| 1d2153b253 | |||
| 005b798688 | |||
| b6d4c0e6a6 | |||
| 3854db9330 | |||
| fc181bda7c | |||
| fb822d20b6 | |||
| 0e708dde0f | |||
| 9a915c55d2 | |||
| 6887f85e6a | |||
| bb95e8ccbd | |||
| 41a4a94255 |
@@ -26,6 +26,7 @@ export default {
|
||||
},
|
||||
edgeToEdgeEnabled: true,
|
||||
package: 'com.bip.hipmimobileapp',
|
||||
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
||||
},
|
||||
|
||||
web: {
|
||||
@@ -36,6 +37,7 @@ export default {
|
||||
|
||||
plugins: [
|
||||
'expo-router',
|
||||
'expo-web-browser',
|
||||
[
|
||||
'expo-splash-screen',
|
||||
{
|
||||
@@ -67,5 +69,6 @@ export default {
|
||||
},
|
||||
// Tambahkan environment variables ke sini
|
||||
API_BASE_URL: process.env.API_BASE_URL,
|
||||
BASE_URL: process.env.BASE_URL,
|
||||
},
|
||||
};
|
||||
@@ -1,9 +1,13 @@
|
||||
import { BackButton, ViewWrapper } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { FontAwesome } from "@expo/vector-icons";
|
||||
import { Stack } from "expo-router";
|
||||
import { BackButton } from "@/components";
|
||||
import PdfViewer from "@/components/_ShareComponent/PdfViewer";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function FileScreen() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const url = API_STRORAGE.GET({ fileId: id as string });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
@@ -12,14 +16,9 @@ export default function FileScreen() {
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<FontAwesome
|
||||
name="file-pdf-o"
|
||||
size={300}
|
||||
style={{ alignSelf: "center" }}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
<SafeAreaView style={{ flex: 1 }} edges={["bottom"]}>
|
||||
<PdfViewer uri={url} />
|
||||
</SafeAreaView>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,14 +118,13 @@ export default function UserLayout() {
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
{/* <Stack.Screen
|
||||
name="collaboration/[id]/detail-participant"
|
||||
options={{
|
||||
title: "Partisipasi Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
/> */}
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/edit"
|
||||
options={{
|
||||
@@ -133,6 +132,20 @@ export default function UserLayout() {
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/create-pacticipants"
|
||||
options={{
|
||||
title: "Ajukan Partisipasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/select-of-participants"
|
||||
options={{
|
||||
title: "Pilih Partisipan",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Collaboration Section ========= */}
|
||||
|
||||
@@ -509,6 +522,13 @@ export default function UserLayout() {
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/archive"
|
||||
options={{
|
||||
title: "Arsip Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Job Section ========= */}
|
||||
|
||||
|
||||
@@ -1,38 +1,96 @@
|
||||
import { BaseBox, Grid, TextCustom } from "@/components";
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BaseBox,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiCollaborationGetAll } from "@/service/api-client/api-collaboration";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState, useCallback } from "react";
|
||||
|
||||
export default function CollaborationGroup() {
|
||||
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [user?.id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiCollaborationGetAll({
|
||||
category: "group",
|
||||
authorId: user?.id,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingBlock={5}
|
||||
href={`/collaboration/${index}/${generateProjectName()}/room-chat`}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom bold>{generateProjectName()}</TextCustom>
|
||||
<TextCustom size="small">2 Anggota</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : (
|
||||
<StackCustom>
|
||||
{listData?.map((item: any, index: any) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingBlock={5}
|
||||
href={`/collaboration/${item?.ProjectCollaboration_RoomChat?.id}/${item?.ProjectCollaboration_RoomChat?.name}/room-chat`}
|
||||
>
|
||||
<Feather name="chevron-right" size={20} color={MainColor.white} />
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextCustom bold>
|
||||
{item?.ProjectCollaboration_RoomChat?.name}
|
||||
</TextCustom>
|
||||
<TextCustom size="small">
|
||||
{
|
||||
item?.ProjectCollaboration_RoomChat
|
||||
?.ProjectCollaboration_AnggotaRoomChat?.length
|
||||
}{" "}
|
||||
Anggota
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
<Feather
|
||||
name="chevron-right"
|
||||
size={20}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function generateProjectName() {
|
||||
const adjectives = [
|
||||
"Blue",
|
||||
@@ -65,4 +123,4 @@ function generateProjectName() {
|
||||
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
||||
|
||||
return randomAdjective + randomNoun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,40 @@
|
||||
import { FloatingButton, ViewWrapper } from "@/components";
|
||||
import {
|
||||
FloatingButton,
|
||||
LoaderCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
import { apiCollaborationGetAll } from "@/service/api-client/api-collaboration";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function CollaborationBeranda() {
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiCollaborationGetAll({
|
||||
category: "beranda",
|
||||
});
|
||||
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
@@ -15,13 +47,19 @@ export default function CollaborationBeranda() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
href={`/collaboration/${index}`}
|
||||
/>
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index}
|
||||
href={`/collaboration/${item.id}`}
|
||||
data={item}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,46 @@
|
||||
import { ButtonCustom, Spacing } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { ButtonCustom, LoaderCustom, Spacing, TextCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Collaboration_BoxPublishSection from "@/screens/Collaboration/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
import { apiCollaborationGetAll } from "@/service/api-client/api-collaboration";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function CollaborationParticipans() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"participant"
|
||||
const [activeCategory, setActiveCategory] = useState<
|
||||
"participant" | "my-project"
|
||||
>("participant");
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [activeCategory])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiCollaborationGetAll({
|
||||
category:
|
||||
activeCategory === "participant" ? "participant" : "my-project",
|
||||
authorId: user?.id,
|
||||
});
|
||||
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
@@ -41,13 +72,13 @@ export default function CollaborationParticipans() {
|
||||
<Spacing width={"2%"} />
|
||||
<ButtonCustom
|
||||
backgroundColor={
|
||||
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
|
||||
activeCategory === "my-project" ? MainColor.yellow : AccentColor.blue
|
||||
}
|
||||
textColor={
|
||||
activeCategory === "main" ? MainColor.black : MainColor.white
|
||||
activeCategory === "my-project" ? MainColor.black : MainColor.white
|
||||
}
|
||||
style={{ width: "49%" }}
|
||||
onPress={() => handlePress("main")}
|
||||
onPress={() => handlePress("my-project")}
|
||||
>
|
||||
Proyek Saya
|
||||
</ButtonCustom>
|
||||
@@ -56,22 +87,27 @@ export default function CollaborationParticipans() {
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter headerComponent={headerComponent}>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index.toString()}
|
||||
id={index.toString()}
|
||||
username={` ${
|
||||
activeCategory === "participant"
|
||||
? "Partisipasi Proyek"
|
||||
: "Proyek Saya"
|
||||
}`}
|
||||
href={
|
||||
activeCategory === "participant"
|
||||
? `/collaboration/${index}/detail-participant`
|
||||
: `/collaboration/${index}/detail-project-main`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : activeCategory === "participant" ? (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index.toString()}
|
||||
data={item?.ProjectCollaboration}
|
||||
href={`/collaboration/${item?.ProjectCollaboration?.id}/detail-participant`}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<Collaboration_BoxPublishSection
|
||||
key={index.toString()}
|
||||
data={item}
|
||||
href={`/collaboration/${item?.id}/detail-project-main`}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,41 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
BaseBox,
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import { apiCollaborationGroup } from "@/service/api-client/api-collaboration";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useState, useCallback } from "react";
|
||||
|
||||
export default function CollaborationRoomInfo() {
|
||||
const { id, detail } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>();
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiCollaborationGroup({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
@@ -24,7 +48,7 @@ export default function CollaborationRoomInfo() {
|
||||
<ViewWrapper>
|
||||
<BoxWithHeaderSection>
|
||||
<StackCustom>
|
||||
{listData.map((item, index) => (
|
||||
{listData({ data }).map((item, index) => (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={4}>
|
||||
<TextCustom bold>{item.title}</TextCustom>
|
||||
@@ -37,37 +61,42 @@ export default function CollaborationRoomInfo() {
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
|
||||
<BoxWithHeaderSection>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<AvatarUsernameAndOtherComponent key={index} avatarHref={`/profile/${index}`} />
|
||||
))}
|
||||
</BoxWithHeaderSection>
|
||||
<BaseBox>
|
||||
<StackCustom gap={10}>
|
||||
{data?.ProjectCollaboration_AnggotaRoomChat?.map(
|
||||
(item: any, index: number) => (
|
||||
<AvatarUsernameAndOtherComponent
|
||||
key={index}
|
||||
avatarHref={`/profile/${item?.User?.Profile?.id}`}
|
||||
name={item?.User?.username}
|
||||
avatar={item?.User?.Profile?.imageId}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
const listData = ({ data }: { data: any }) => [
|
||||
{
|
||||
title: "Judul Proyek",
|
||||
value: "Judul Proyek",
|
||||
value: data?.ProjectCollaboration?.title || "-",
|
||||
},
|
||||
{
|
||||
title: "Industri",
|
||||
value: "Pilihan Industri",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value: "Deskripsi Proyek",
|
||||
value:
|
||||
data?.ProjectCollaboration?.ProjectCollaborationMaster_Industri?.name ||
|
||||
"-",
|
||||
},
|
||||
{
|
||||
title: "Tujuan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
value: data?.ProjectCollaboration?.purpose || "-",
|
||||
},
|
||||
{
|
||||
title: "Keuntungan Proyek",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
value: data?.ProjectCollaboration?.benefit || "-",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,68 +1,13 @@
|
||||
import {
|
||||
BackButton,
|
||||
BoxButtonOnFooter,
|
||||
Grid,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { BackButton } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import ChatScreen from "@/screens/Collaboration/GroupChatSection";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
|
||||
export default function CollaborationRoomChat() {
|
||||
const { id, detail } = useLocalSearchParams();
|
||||
|
||||
const inputChat = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
{/* <View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||
<TextInputCustom placeholder="Ketik pesan..." />
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.5}
|
||||
onPress={() => console.log("Send")}
|
||||
style={{
|
||||
backgroundColor: AccentColor.blue,
|
||||
padding: 10,
|
||||
borderRadius: 50,
|
||||
}}
|
||||
>
|
||||
<Feather name="send" size={30} color={MainColor.white} />
|
||||
</TouchableOpacity>
|
||||
</View> */}
|
||||
<Grid>
|
||||
<Grid.Col span={9}>
|
||||
<TextInputCustom placeholder="Ketik pesan..." />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={1}>
|
||||
<View />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.5}
|
||||
onPress={() => console.log("Send")}
|
||||
style={{
|
||||
backgroundColor: AccentColor.blue,
|
||||
padding: 10,
|
||||
borderRadius: 50,
|
||||
}}
|
||||
>
|
||||
<Feather
|
||||
name="send"
|
||||
size={30}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
@@ -79,114 +24,8 @@ export default function CollaborationRoomChat() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper footerComponent={inputChat()}>
|
||||
{dummyData.map((item, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={[
|
||||
styles.messageRow,
|
||||
item.role === 1 ? styles.rightAlign : styles.leftAlign,
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
styles.bubble,
|
||||
item.role === 1 ? styles.bubbleRight : styles.bubbleLeft,
|
||||
]}
|
||||
>
|
||||
<TextCustom style={styles.sender}>{item.nama}</TextCustom>
|
||||
<TextCustom style={styles.message}>{item.chat}</TextCustom>
|
||||
<TextCustom style={styles.time}>
|
||||
{new Date(item.time).toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})}
|
||||
</TextCustom>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
{/* <TextInputCustom placeholder="Ketik pesan..." />
|
||||
<Spacing/> */}
|
||||
</ViewWrapper>
|
||||
|
||||
<ChatScreen id={id as string} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Hai! Kamu udah lihat dokumen proyek yang baru?",
|
||||
time: "2025-07-24T09:01:15Z",
|
||||
},
|
||||
{
|
||||
nama: "Rafi",
|
||||
role: 2,
|
||||
chat: "Halo! Iya, aku baru aja baca. Kayaknya kita harus revisi bagian akhir deh.",
|
||||
time: "2025-07-24T09:02:03Z",
|
||||
},
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Setuju. Aku juga kurang sreg sama penutupnya.",
|
||||
time: "2025-07-24T09:02:45Z",
|
||||
},
|
||||
{
|
||||
nama: "Rafi",
|
||||
role: 2,
|
||||
chat: "Oke, aku coba edit malam ini ya. Nanti aku share ulang versinya.",
|
||||
time: "2025-07-24T09:03:10Z",
|
||||
},
|
||||
{
|
||||
nama: "Dina",
|
||||
role: 1,
|
||||
chat: "Siap, makasih ya. Jangan begadang!",
|
||||
time: "2025-07-24T09:03:30Z",
|
||||
},
|
||||
];
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
messageRow: {
|
||||
flexDirection: "row",
|
||||
marginBottom: 12,
|
||||
},
|
||||
rightAlign: {
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
leftAlign: {
|
||||
justifyContent: "flex-start",
|
||||
},
|
||||
bubble: {
|
||||
maxWidth: "75%",
|
||||
padding: 10,
|
||||
borderRadius: 12,
|
||||
},
|
||||
bubbleRight: {
|
||||
backgroundColor: "#DCF8C6", // hijau muda
|
||||
borderTopRightRadius: 0,
|
||||
},
|
||||
bubbleLeft: {
|
||||
backgroundColor: "#F0F0F0", // abu-abu terang
|
||||
borderTopLeftRadius: 0,
|
||||
},
|
||||
sender: {
|
||||
fontSize: 12,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 2,
|
||||
color: "#555",
|
||||
},
|
||||
message: {
|
||||
fontSize: 15,
|
||||
color: "#000",
|
||||
},
|
||||
time: {
|
||||
fontSize: 10,
|
||||
color: "#888",
|
||||
textAlign: "right",
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
ButtonCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiCollaborationCreatePartisipasi } from "@/service/api-client/api-collaboration";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function CollaborationCreatePartisipans() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [description, setDescription] = useState("");
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const handlerSubmitParticipans = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiCollaborationCreatePartisipasi({
|
||||
id: id as string,
|
||||
data: {
|
||||
authorId: user?.id,
|
||||
description,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Data berhasil disimpan",
|
||||
});
|
||||
router.replace(`/collaboration/${id}/list-of-participants`);
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal menyimpan data",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<TextAreaCustom
|
||||
// label="Deskripsi"
|
||||
placeholder="Masukan deskripsi diri anda .."
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
disabled={description.length === 0}
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Simpan data deskripsi",
|
||||
message: "Apakah anda sudah yakin ingin menyimpan data ini ?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Simpan",
|
||||
onPressRight: () => {
|
||||
handlerSubmitParticipans();
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -1,26 +1,54 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { apiCollaborationGetOne } from "@/service/api-client/api-collaboration";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function CollaborationDetailParticipant() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiCollaborationGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Detail Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerParticipant(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<BaseBox style={{ height: 500 }}>
|
||||
<Collaboration_BoxDetailSection data={data} />
|
||||
{/* <BaseBox style={{ height: 500 }}>
|
||||
<TextCustom align="center" bold size="large">
|
||||
Partisipan
|
||||
</TextCustom>
|
||||
@@ -39,13 +67,33 @@ export default function CollaborationDetailParticipant() {
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</BaseBox>
|
||||
</BaseBox> */}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <Ionicons name="people" size={24} color="white" />,
|
||||
label: "Daftar Partisipan",
|
||||
path: `/collaboration/${id}/list-of-participants`,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
router.push(item.path as any);
|
||||
setOpenDrawerParticipant(false);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* <DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi Diri</TextCustom>
|
||||
@@ -56,7 +104,7 @@ export default function CollaborationDetailParticipant() {
|
||||
Temporibus iusto soluta necessitatibus.
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</DrawerCustom> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,65 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import Collaboration_MainParticipanSelectedSection from "@/screens/Collaboration/ProjectMainSelectedSection";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
apiCollaborationGetOne
|
||||
} from "@/service/api-client/api-collaboration";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function CollaborationDetailProjectMain() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openDrawerParticipant, setOpenDrawerParticipant] = useState(false);
|
||||
const [selected, setSelected] = useState<(string | number)[]>([]);
|
||||
const [data, setData] = useState<any>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
const handleEdit = () => {
|
||||
console.log("Edit collaboration");
|
||||
router.push("/(application)/(user)/collaboration/(id)/edit");
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const handlerLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
await onLoadData();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiCollaborationGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (item: any) => {
|
||||
console.log("item :", item);
|
||||
router.push(item.path);
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -37,34 +72,21 @@ export default function CollaborationDetailProjectMain() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
<Collaboration_MainParticipanSelectedSection
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
setOpenDrawerParticipant={setOpenDrawerParticipant}
|
||||
/>
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<Collaboration_BoxDetailSection data={data} />
|
||||
{/* <Collaboration_MainParticipanSelectedSection
|
||||
selected={selected}
|
||||
setSelected={setSelected}
|
||||
setOpenDrawerParticipant={setOpenDrawerParticipant}
|
||||
listData={listData as any}
|
||||
/> */}
|
||||
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Buat Grup",
|
||||
message:
|
||||
"Apakah anda yakin ingin membuat grup untuk proyek ini ?",
|
||||
textLeft: "Tidak",
|
||||
textRight: "Ya",
|
||||
onPressLeft: () => {},
|
||||
onPressRight: () => {
|
||||
router.navigate(
|
||||
"/(application)/(user)/collaboration/(tabs)/group"
|
||||
);
|
||||
console.log("selected :", selected);
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buat Grup
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
@@ -76,31 +98,20 @@ export default function CollaborationDetailProjectMain() {
|
||||
data={[
|
||||
{
|
||||
label: "Edit",
|
||||
path: "/(application)/(user)/collaboration/(tabs)/group",
|
||||
path: `/(application)/(user)/collaboration/${id}/edit`,
|
||||
icon: <IconEdit />,
|
||||
},
|
||||
{
|
||||
label: "Pilih Partisipan",
|
||||
path: `/(application)/(user)/collaboration/${id}/select-of-participants`,
|
||||
icon: <MaterialIcons name="checklist" size={24} color="white" />,
|
||||
},
|
||||
]}
|
||||
onPressItem={(item) => {
|
||||
handleEdit();
|
||||
onPressItem={(item: any) => {
|
||||
handleSubmit(item);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerParticipant}
|
||||
closeDrawer={() => setOpenDrawerParticipant(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi Diri</TextCustom>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Commodi,
|
||||
itaque adipisci. Voluptas, sed quod! Ad facere labore voluptates,
|
||||
neque quidem aut reprehenderit ducimus mollitia quisquam temporibus!
|
||||
Temporibus iusto soluta necessitatibus.
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +1,171 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import {
|
||||
apiCollaborationEditData,
|
||||
apiCollaborationGetOne,
|
||||
} from "@/service/api-client/api-collaboration";
|
||||
import { apiMasterCollaborationType } from "@/service/api-client/api-master";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function CollaborationEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
console.log("id :", id);
|
||||
const [data, setData] = useState<any>();
|
||||
const [listMaster, setListMaster] = useState<any[]>([]);
|
||||
const [loadingData, setLoadingData] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoadingData(true);
|
||||
await onLoadData();
|
||||
await onLoadMaster();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingData(false);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiCollaborationGetOne({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
async function onLoadMaster() {
|
||||
try {
|
||||
const response = await apiMasterCollaborationType();
|
||||
setListMaster(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
}
|
||||
|
||||
const handlerSubmitUpdate = async () => {
|
||||
if (
|
||||
!data?.title ||
|
||||
!data?.lokasi ||
|
||||
!data?.projectCollaborationMaster_IndustriId ||
|
||||
!data?.purpose ||
|
||||
!data?.benefit
|
||||
) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Harap isi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiCollaborationEditData({
|
||||
id: id as string,
|
||||
data: data,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: response.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
|
||||
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={[
|
||||
{ label: "Industri 1", value: "industri-1" },
|
||||
{ label: "Industri 2", value: "industri-2" },
|
||||
{ label: "Industri 3", value: "industri-3" },
|
||||
]}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
{loadingData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul"
|
||||
placeholder="Masukan judul"
|
||||
required
|
||||
value={data?.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukan lokasi"
|
||||
required
|
||||
value={data?.lokasi}
|
||||
onChangeText={(value) => setData({ ...data, lokasi: value })}
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={listMaster?.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data?.projectCollaborationMaster_IndustriId}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, projectCollaborationMaster_IndustriId: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data?.purpose}
|
||||
onChangeText={(value) => setData({ ...data, purpose: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data?.benefit}
|
||||
onChangeText={(value) => setData({ ...data, benefit: value })}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
title="Update"
|
||||
onPress={() => {
|
||||
console.log("Update proyek");
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
title="Update"
|
||||
onPress={() => {
|
||||
handlerSubmitUpdate();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,75 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
InformationBox,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import {
|
||||
apiCollaborationGetOne,
|
||||
apiCollaborationGetParticipants,
|
||||
} from "@/service/api-client/api-collaboration";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function CollaborationDetail() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPartisipasi, setOpenDrawerPartisipasi] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
const [openDrawerMenu, setOpenDrawerMenu] = useState(false);
|
||||
const [isParticipant, setIsParticipant] = useState(false);
|
||||
const [loadingIsParticipant, setLoadingIsParticipant] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
onLoadParticipants();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiCollaborationGetOne({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadParticipants = async () => {
|
||||
try {
|
||||
setLoadingIsParticipant(true);
|
||||
const response = await apiCollaborationGetParticipants({
|
||||
category: "check-participant",
|
||||
id: id as string,
|
||||
authorId: user?.id,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setIsParticipant(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingIsParticipant(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
@@ -29,15 +82,35 @@ export default function CollaborationDetail() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Collaboration_BoxDetailSection id={id as string} />
|
||||
|
||||
<ButtonCustom onPress={() => setOpenDrawerPartisipasi(true)}>
|
||||
Partisipasi
|
||||
</ButtonCustom>
|
||||
{!data && !isParticipant ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
{user?.id === data?.Author?.id && (
|
||||
<InformationBox
|
||||
text={
|
||||
"Tombol partisipasi hanya muncul pada proyek milik orang lain"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Collaboration_BoxDetailSection data={data} />
|
||||
{user?.id !== data?.Author?.id && (
|
||||
<ButtonCustom
|
||||
disabled={isParticipant || loadingIsParticipant}
|
||||
onPress={() => {
|
||||
router.push(`/collaboration/${id}/create-pacticipants`);
|
||||
// setOpenDrawerPartisipasi(true);
|
||||
}}
|
||||
>
|
||||
{isParticipant ? "Anda telah berpartisipasi" : "Partisipasi"}
|
||||
</ButtonCustom>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Partisipasi */}
|
||||
<DrawerCustom
|
||||
{/* <DrawerCustom
|
||||
isVisible={openDrawerPartisipasi}
|
||||
closeDrawer={() => setOpenDrawerPartisipasi(false)}
|
||||
height={300}
|
||||
@@ -48,6 +121,8 @@ export default function CollaborationDetail() {
|
||||
required
|
||||
showCount
|
||||
maxLength={500}
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
@@ -58,19 +133,21 @@ export default function CollaborationDetail() {
|
||||
message: "Apakah anda sudah yakin ingin menyimpan data ini ?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Simpan",
|
||||
onPressRight: () => router.replace(`/collaboration/(tabs)/group`),
|
||||
onPressRight: () => {
|
||||
handlerSubmitParticipans();
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</DrawerCustom>
|
||||
</DrawerCustom> */}
|
||||
|
||||
{/* Drawer Menu */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawerMenu}
|
||||
closeDrawer={() => setOpenDrawerMenu(false)}
|
||||
height={250}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
|
||||
@@ -1,38 +1,79 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
DrawerCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BaseBox,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { apiCollaborationGetParticipants } from "@/service/api-client/api-collaboration";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ScrollView } from "react-native";
|
||||
|
||||
export default function CollaborationListOfParticipants() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [description, setDescription] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiCollaborationGetParticipants({
|
||||
category: "list",
|
||||
id: id as string,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingBlock={5}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${id}`}
|
||||
rightComponent={
|
||||
<Feather
|
||||
name="chevron-right"
|
||||
size={24}
|
||||
color="white"
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada partisipan</TextCustom>
|
||||
) : (
|
||||
listData?.map((item: any, index: number) => (
|
||||
<BaseBox key={index} paddingBlock={5}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={item?.User?.Profile?.imageId}
|
||||
avatarHref={`/profile/${item?.User?.Profile?.id}`}
|
||||
name={item?.User?.username}
|
||||
rightComponent={
|
||||
<Feather
|
||||
name="chevron-right"
|
||||
size={24}
|
||||
color="white"
|
||||
onPress={() => {
|
||||
setDescription(item?.deskripsi_diri);
|
||||
setOpenDrawer(true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer */}
|
||||
@@ -44,34 +85,7 @@ export default function CollaborationListOfParticipants() {
|
||||
<TextCustom bold>Deskripsi diri</TextCustom>
|
||||
<BaseBox>
|
||||
<ScrollView style={{ height: "80%" }}>
|
||||
<TextCustom>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.Lorem
|
||||
ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut iqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.Lorem ipsum dolor sit amet,
|
||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua.
|
||||
</TextCustom>
|
||||
<TextCustom>{description}</TextCustom>
|
||||
</ScrollView>
|
||||
</BaseBox>
|
||||
<Spacing />
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
AvatarComp,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
CheckboxCustom,
|
||||
CheckboxGroup,
|
||||
DrawerCustom,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import ModalCustom from "@/components/Modal/ModalCustom";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
apiCollaborationCreateGroup,
|
||||
apiCollaborationGetParticipants,
|
||||
} from "@/service/api-client/api-collaboration";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function CollaborationSelectOfParticipants() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [description, setDescription] = useState("");
|
||||
const [selected, setSelected] = useState<(string | number)[]>([]);
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [nameGroup, setNameGroup] = useState("");
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiCollaborationGetParticipants({
|
||||
category: "list",
|
||||
id: id as string,
|
||||
});
|
||||
// console.log("response :", JSON.stringify(response.data, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerCreateGroup = async () => {
|
||||
if (_.isEmpty(nameGroup)) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Nama grup tidak boleh kosong",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiCollaborationCreateGroup({
|
||||
id: id as string,
|
||||
data: {
|
||||
authorId: user?.id,
|
||||
nameGroup: nameGroup,
|
||||
listSelect: selected,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Grup berhasil dibuat",
|
||||
});
|
||||
router.push("/(application)/(user)/collaboration/(tabs)/group");
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal membuat grup",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
disabled={_.isEmpty(selected)}
|
||||
onPress={() => {
|
||||
setOpenModal(true);
|
||||
setNameGroup("");
|
||||
}}
|
||||
>
|
||||
Buat Grup
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
footerComponent={_.isEmpty(listData) ? null : handlerSubmit()}
|
||||
>
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada partisipan</TextCustom>
|
||||
) : (
|
||||
<StackCustom>
|
||||
<TextCustom size="default" color="red" bold>
|
||||
*{" "}
|
||||
<TextCustom size="small" semiBold>
|
||||
Pilih user yang akan menjadi tim proyek anda
|
||||
</TextCustom>
|
||||
</TextCustom>
|
||||
|
||||
<CheckboxGroup
|
||||
value={selected}
|
||||
onChange={(val: any) => {
|
||||
console.log("val :", val);
|
||||
setSelected(val);
|
||||
}}
|
||||
>
|
||||
{listData?.map((item: any, index: any) => (
|
||||
<View key={index}>
|
||||
<Grid key={index}>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CheckboxCustom valueKey={item?.User?.id} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<AvatarComp
|
||||
size="base"
|
||||
fileId={item?.User?.Profile?.imageId}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom bold truncate>
|
||||
{item?.User?.username}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<MaterialIcons
|
||||
name="notes"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setDescription(item?.deskripsi_diri);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
))}
|
||||
</CheckboxGroup>
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
<ModalCustom isVisible={openModal}>
|
||||
<StackCustom gap={0}>
|
||||
<TextAreaCustom
|
||||
placeholder="Nama Grup"
|
||||
value={nameGroup}
|
||||
onChangeText={(val) => setNameGroup(val)}
|
||||
/>
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom
|
||||
backgroundColor="gray"
|
||||
onPress={() => {
|
||||
setOpenModal(false);
|
||||
}}
|
||||
>
|
||||
Batal
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
disabled={_.isEmpty(nameGroup)}
|
||||
onPress={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Buat Grup",
|
||||
message:
|
||||
"Apakah anda yakin ingin membuat grup untuk proyek ini ?",
|
||||
textLeft: "Tidak",
|
||||
textRight: "Ya",
|
||||
onPressLeft: () => {},
|
||||
onPressRight: () => {
|
||||
handlerCreateGroup();
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</ModalCustom>
|
||||
|
||||
{/* Drawer */}
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom bold>Deskripsi diri</TextCustom>
|
||||
<BaseBox>
|
||||
<ScrollView style={{ height: "80%" }}>
|
||||
<TextCustom>{description}</TextCustom>
|
||||
</ScrollView>
|
||||
</BaseBox>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,53 +1,174 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
SelectCustom,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiCollaborationCreate } from "@/service/api-client/api-collaboration";
|
||||
import { apiMasterCollaborationType } from "@/service/api-client/api-master";
|
||||
import { router } from "expo-router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface CollaborationCreateProps {
|
||||
title?: string;
|
||||
lokasi?: string;
|
||||
purpose?: string;
|
||||
benefit?: string;
|
||||
projectCollaborationMaster_IndustriId?: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export default function CollaborationCreate() {
|
||||
const { user } = useAuth();
|
||||
const [listMaster, setListMaster] = useState<any>([]);
|
||||
const [loadingMaster, setLoadingMaster] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = React.useState<CollaborationCreateProps>({
|
||||
title: "",
|
||||
lokasi: "",
|
||||
purpose: "",
|
||||
benefit: "",
|
||||
projectCollaborationMaster_IndustriId: "",
|
||||
userId: "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
onLoadMaster();
|
||||
}, []);
|
||||
|
||||
async function onLoadMaster() {
|
||||
try {
|
||||
setLoadingMaster(true);
|
||||
const response = await apiMasterCollaborationType();
|
||||
setListMaster(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingMaster(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
if (
|
||||
!data?.title ||
|
||||
!data?.lokasi ||
|
||||
!data?.purpose ||
|
||||
!data?.benefit ||
|
||||
!data?.projectCollaborationMaster_IndustriId
|
||||
) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Harap isi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newData: CollaborationCreateProps = {
|
||||
title: data?.title,
|
||||
lokasi: data?.lokasi,
|
||||
purpose: data?.purpose,
|
||||
benefit: data?.benefit,
|
||||
projectCollaborationMaster_IndustriId:
|
||||
data?.projectCollaborationMaster_IndustriId,
|
||||
userId: user?.id,
|
||||
};
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await apiCollaborationCreate({ data: newData });
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil",
|
||||
text2: response.message,
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: response.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom label="Judul" placeholder="Masukan judul" required />
|
||||
<TextInputCustom label="Lokasi" placeholder="Masukan lokasi" required />
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={[
|
||||
{ label: "Industri 1", value: "industri-1" },
|
||||
{ label: "Industri 2", value: "industri-2" },
|
||||
{ label: "Industri 3", value: "industri-3" },
|
||||
]}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul"
|
||||
placeholder="Masukan judul"
|
||||
required
|
||||
value={data?.title}
|
||||
onChangeText={(value: any) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukan lokasi"
|
||||
required
|
||||
value={data?.lokasi}
|
||||
onChangeText={(value: any) => setData({ ...data, lokasi: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Pilih Industri"
|
||||
data={listMaster?.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data?.projectCollaborationMaster_IndustriId}
|
||||
onChange={(value: any) => {
|
||||
console.log(value);
|
||||
setData({
|
||||
...data,
|
||||
projectCollaborationMaster_IndustriId: value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
title="Simpan"
|
||||
onPress={() => {
|
||||
console.log("Simpan proyek");
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
</StackCustom>
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Tujuan Proyek"
|
||||
placeholder="Masukan tujuan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data?.purpose}
|
||||
onChangeText={(value: any) => setData({ ...data, purpose: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
required
|
||||
label="Keuntungan Proyek"
|
||||
placeholder="Masukan keuntungan proyek"
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data?.benefit}
|
||||
onChangeText={(value: any) => setData({ ...data, benefit: value })}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
title="Simpan"
|
||||
onPress={() => handlerSubmit()}
|
||||
/>
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,43 +1,115 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarCustom,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import React from "react";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
apiEventGetAll
|
||||
} from "@/service/api-client/api-event";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import React, { useCallback, useState } from "react";
|
||||
|
||||
export default function EventContribution() {
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [isLoadList, setIsLoadList] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [user?.id])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
setIsLoadList(true);
|
||||
const response = await apiEventGetAll({
|
||||
category: "contribution",
|
||||
userId: user?.id,
|
||||
});
|
||||
console.log("[DATA] ", JSON.stringify(response.data, null, 2));
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
|
||||
// const responseListParticipants = await apiEventListOfParticipants({
|
||||
// id: response?.data?.Event?.id,
|
||||
// });
|
||||
// console.log(
|
||||
// "[LIST PARTICIPANTS]",
|
||||
// JSON.stringify(responseListParticipants.data, null, 2)
|
||||
// );
|
||||
// if (responseListParticipants.success) {
|
||||
// setListParticipants(responseListParticipants.data);
|
||||
// }
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadList(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BoxWithHeaderSection key={index} href={`/event/${index}/contribution`}>
|
||||
<StackCustom>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatarHref={`/profile/${index}`}
|
||||
rightComponent={
|
||||
<TextCustom truncate>
|
||||
{new Date().toLocaleDateString()}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
{isLoadList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Belum ada kontribusi</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<BoxWithHeaderSection
|
||||
key={index}
|
||||
href={`/event/${item?.Event?.id}/contribution`}
|
||||
>
|
||||
<StackCustom>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={item?.Event?.Author?.Profile?.imageId}
|
||||
avatarHref={`/profile/${item?.Event?.Author?.Profile?.id}`}
|
||||
name={item?.Event?.Author?.username}
|
||||
rightComponent={
|
||||
<TextCustom truncate>
|
||||
{dateTimeView({
|
||||
date: item?.Event?.tanggal,
|
||||
withoutTime: true,
|
||||
})}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
|
||||
<TextCustom bold align="center" size="xlarge">
|
||||
Judul Event Disini
|
||||
</TextCustom>
|
||||
<TextCustom bold align="center" size="xlarge" truncate={2}>
|
||||
{item?.Event?.title}
|
||||
</TextCustom>
|
||||
<Spacing height={0} />
|
||||
|
||||
<Grid>
|
||||
{Array.from({ length: 4 }).map((_, index2) => (
|
||||
<Grid.Col span={3} key={index2}>
|
||||
<AvatarCustom size="sm" href={`/profile/${index2}`} />
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
))}
|
||||
{/* <Grid>
|
||||
{item?.Event?.Event_Peserta?.map(
|
||||
(item2: any, index2: number) => (
|
||||
<Grid.Col
|
||||
style={{ alignItems: "center" }}
|
||||
span={12 / item?.Event?.Event_Peserta?.length}
|
||||
key={index2}
|
||||
>
|
||||
<AvatarComp
|
||||
size="base"
|
||||
href={`/profile/${item2?.User?.Profile?.id}`}
|
||||
fileId={item2?.User?.Profile?.imageId}
|
||||
/>
|
||||
</Grid.Col>
|
||||
)
|
||||
)}
|
||||
</Grid> */}
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,41 @@
|
||||
import { ButtonCustom, Spacing, TextCustom } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { ButtonCustom, LoaderCustom, Spacing, TextCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
import { apiEventGetAll } from "@/service/api-client/api-event";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function EventHistory() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [isLoadList, setIsLoadList] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData({ userId: user?.id });
|
||||
}, [user?.id, activeCategory]);
|
||||
|
||||
async function onLoadData({ userId }: { userId?: string }) {
|
||||
try {
|
||||
setIsLoadList(true);
|
||||
const response = await apiEventGetAll({
|
||||
category: activeCategory === "all" ? "all-history" : "my-history",
|
||||
userId: userId,
|
||||
});
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadList(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
@@ -52,17 +81,24 @@ export default function EventHistory() {
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={headerComponent} hideFooter>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index.toString()}
|
||||
id={index.toString()}
|
||||
username={`Riwayat ${activeCategory === "main" ? "Saya" : "Semua"}`}
|
||||
rightComponentAvatar={
|
||||
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
|
||||
}
|
||||
href={`/event/${index}/history`}
|
||||
/>
|
||||
))}
|
||||
{isLoadList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Belum ada riwayat</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index.toString()}
|
||||
data={item}
|
||||
rightComponentAvatar={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
|
||||
</TextCustom>
|
||||
}
|
||||
href={`/event/${item.id}/history`}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
import { LoaderCustom, TextCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
import { apiEventGetAll } from "@/service/api-client/api-event";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function EventBeranda() {
|
||||
const [listData, setListData] = useState([]);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiEventGetAll({category: "beranda"});
|
||||
// console.log("Response", JSON.stringify(response.data, null, 2));
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
@@ -11,13 +38,24 @@ export default function EventBeranda() {
|
||||
<FloatingButton onPress={() => router.push("/event/create")} />
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index}
|
||||
id={index.toString()}
|
||||
href={`/event/${index}/publish`}
|
||||
/>
|
||||
))}
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Belum ada event</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index) => (
|
||||
<Event_BoxPublishSection
|
||||
key={index}
|
||||
href={`/event/${item.id}/publish`}
|
||||
data={item}
|
||||
rightComponentAvatar={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,57 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
BoxWithHeaderSection,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { useState } from "react";
|
||||
import { apiEventGetByStatus } from "@/service/api-client/api-event";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function EventStatus() {
|
||||
const id = "test-id-event";
|
||||
|
||||
const { user } = useAuth();
|
||||
const id = user?.id || "";
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
const [listData, setListData] = useState([]);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [activeCategory, id])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiEventGetByStatus({
|
||||
id: id!,
|
||||
status: activeCategory!,
|
||||
});
|
||||
// console.log("Response", JSON.stringify(response.data, null, 2));
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
const scrollComponent = (
|
||||
const tabsComponent = (
|
||||
<ScrollableCustom
|
||||
data={dummyMasterStatus.map((e, i) => ({
|
||||
id: i,
|
||||
@@ -34,30 +64,36 @@ export default function EventStatus() {
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewWrapper headerComponent={scrollComponent}>
|
||||
<BoxWithHeaderSection href={`/event/${id}/${activeCategory}/detail-event`}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<Grid>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom truncate bold>
|
||||
Lorem ipsum,{" "}
|
||||
<TextCustom color="green">{activeCategory}</TextCustom> dolor
|
||||
sit amet consectetur adipisicing elit.
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
<ViewWrapper headerComponent={tabsComponent}>
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, i) => (
|
||||
<BoxWithHeaderSection
|
||||
key={i}
|
||||
href={`/event/${item.id }/${activeCategory}/detail-event`}
|
||||
>
|
||||
<StackCustom gap={"xs"}>
|
||||
<Grid>
|
||||
<Grid.Col span={8}>
|
||||
<TextCustom truncate bold>
|
||||
{item?.title}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||
<TextCustom>
|
||||
{new Date(item?.tanggal).toLocaleDateString()}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<TextCustom truncate={2}>
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur
|
||||
eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora,
|
||||
atque. Aperiam minima asperiores dicta perferendis quis adipisci,
|
||||
dolore optio porro!
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
<TextCustom truncate={2}>{item?.deskripsi}</TextCustom>
|
||||
</StackCustom>
|
||||
</BoxWithHeaderSection>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BaseBox,
|
||||
DotButton,
|
||||
@@ -11,17 +12,64 @@ import {
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_AlertButtonStatusSection from "@/screens/Event/AlertButtonStatusSection";
|
||||
import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection";
|
||||
import { menuDrawerDraftEvent } from "@/screens/Event/menuDrawerDraft";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { apiEventGetOne } from "@/service/api-client/api-event";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function EventDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [openAlert, setOpenAlert] = useState(false);
|
||||
const [openDeleteAlert, setOpenDeleteAlert] = useState(false);
|
||||
// const [openAlert, setOpenAlert] = useState(false);
|
||||
|
||||
const [data, setData] = useState<any>();
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Lokasi",
|
||||
value: data?.lokasi || "-",
|
||||
},
|
||||
{
|
||||
title: "Tipe Acara",
|
||||
value: data?.EventMaster_TipeAcara?.name || "-",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Mulai",
|
||||
value: dateTimeView({ date: data?.tanggal }) || "-",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Berakhir",
|
||||
value: dateTimeView({ date: data?.tanggalSelesai }) || "-",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value: data?.deskripsi || "-",
|
||||
},
|
||||
];
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
@@ -45,7 +93,7 @@ export default function EventDetailStatus() {
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<TextCustom bold align="center" size="xlarge">
|
||||
Judul event {status}
|
||||
{data?.title || "-"}
|
||||
</TextCustom>
|
||||
{listData.map((item, index) => (
|
||||
<Grid key={index}>
|
||||
@@ -60,9 +108,8 @@ export default function EventDetailStatus() {
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
<Event_ButtonStatusSection
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
onOpenAlert={setOpenAlert}
|
||||
onOpenDeleteAlert={setOpenDeleteAlert}
|
||||
/>
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
@@ -70,7 +117,7 @@ export default function EventDetailStatus() {
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerDraftEvent({ id: id as string }) as any}
|
||||
@@ -78,40 +125,6 @@ export default function EventDetailStatus() {
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
<Event_AlertButtonStatusSection
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
openAlert={openAlert}
|
||||
setOpenAlert={setOpenAlert}
|
||||
openDeleteAlert={openDeleteAlert}
|
||||
setOpenDeleteAlert={setOpenDeleteAlert}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const listData = [
|
||||
{
|
||||
title: "Lokasi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
{
|
||||
title: "Tipe Acara",
|
||||
value: "Workshop",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Mulai",
|
||||
value: "Senin, 18 Juli 2025, 10:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Tanggal Berakhir",
|
||||
value: "Selasa, 19 Juli 2025, 12:00 WIB",
|
||||
},
|
||||
{
|
||||
title: "Deskripsi",
|
||||
value:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,20 +1,43 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { apiEventGetOne } from "@/service/api-client/api-event";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function EventDetailContribution() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
@@ -32,18 +55,22 @@ export default function EventDetailContribution() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection />
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<Event_BoxDetailPublishSection data={data} />
|
||||
)}
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
|
||||
@@ -1,106 +1,271 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
|
||||
import {
|
||||
apiEventGetOne,
|
||||
apiEventUpdateData,
|
||||
} from "@/service/api-client/api-event";
|
||||
import { apiMasterEventType } from "@/service/api-client/api-master";
|
||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||
import { router } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function EventEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>();
|
||||
// {
|
||||
// title: "",
|
||||
// lokasi: "",
|
||||
// deskripsi: "",
|
||||
// eventMaster_TipeAcaraId: "",
|
||||
// tanggal: "",
|
||||
// tanggalSelesai: "",
|
||||
// authorId: "",
|
||||
// }
|
||||
const [listTypeEvent, setListTypeEvent] = useState([]);
|
||||
const [selectedDate, setSelectedDate] = useState<
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
>();
|
||||
|
||||
const [selectedEndDate, setSelectedEndDate] = useState<
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
>();
|
||||
|
||||
const handlerSubmit = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
if (selectedDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal belum dipilih");
|
||||
setIsLoadData(true);
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
setSelectedDate(new Date(response.data.tanggal));
|
||||
setSelectedEndDate(new Date(response.data.tanggalSelesai));
|
||||
}
|
||||
|
||||
if (selectedEndDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal berakhir belum dipilih");
|
||||
}
|
||||
|
||||
console.log("Data berhasil terupdate");
|
||||
router.back()
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onLoadMasterEventType();
|
||||
}, []);
|
||||
|
||||
const onLoadMasterEventType = async () => {
|
||||
try {
|
||||
const response = await apiMasterEventType();
|
||||
setListTypeEvent(response.data);
|
||||
} catch (error) {
|
||||
console.log("Error onLoadMasterEventType", error);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = (
|
||||
<ButtonCustom title="Update" onPress={handlerSubmit} />
|
||||
);
|
||||
const validateDate = async () => {
|
||||
if (
|
||||
data?.title === "" ||
|
||||
data?.lokasi === "" ||
|
||||
data?.deskripsi === "" ||
|
||||
data?.eventMaster_TipeAcaraId === ""
|
||||
) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Lengkapi semua data",
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const startDate = new Date(selectedDate as any);
|
||||
const endDate = new Date(selectedEndDate as any);
|
||||
|
||||
if (startDate >= endDate) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Ubah tanggal berakhirnya event",
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
const isValid = await validateDate();
|
||||
if (!isValid) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const newData = {
|
||||
...data,
|
||||
tanggal: new Date(selectedDate as any).toISOString(),
|
||||
tanggalSelesai: new Date(selectedEndDate as any).toISOString(),
|
||||
};
|
||||
|
||||
const response = await apiEventUpdateData({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
return router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const validateDateRange = (
|
||||
selectedDate: string | Date,
|
||||
selectedEndDate: string | Date
|
||||
): { isValid: boolean; error?: string } => {
|
||||
const startDate = new Date(selectedDate);
|
||||
const endDate = new Date(selectedEndDate);
|
||||
|
||||
// Cek apakah tanggal valid
|
||||
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: "Invalid date provided",
|
||||
};
|
||||
}
|
||||
|
||||
if (startDate >= endDate) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: "Ubah tanggal berakhirnya event",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
error: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
placeholder="Masukkan nama event"
|
||||
label="Nama Event"
|
||||
required
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Tipe Event"
|
||||
placeholder="Pilih tipe event"
|
||||
data={masterTypeEvent}
|
||||
onChange={(value) => console.log(value)}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukkan lokasi event"
|
||||
required
|
||||
/>
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
placeholder="Masukkan nama event"
|
||||
label="Nama Event"
|
||||
required
|
||||
value={data?.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
<SelectCustom
|
||||
label="Tipe Event"
|
||||
placeholder="Pilih tipe event"
|
||||
data={listTypeEvent.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data?.eventMaster_TipeAcaraId || ""}
|
||||
onChange={(value) => {
|
||||
console.log(value);
|
||||
setData({ ...data, eventMaster_TipeAcaraId: value });
|
||||
}}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukkan lokasi event"
|
||||
required
|
||||
value={data?.lokasi}
|
||||
onChangeText={(value) => setData({ ...data, lokasi: value })}
|
||||
/>
|
||||
<DateTimePickerCustom
|
||||
minimumDate={new Date(Date.now())}
|
||||
label="Tanggal & Waktu Mulai"
|
||||
required
|
||||
value={selectedDate as any}
|
||||
onChange={(date: any) => {
|
||||
setSelectedDate(date as any);
|
||||
}}
|
||||
/>
|
||||
<StackCustom gap={0}>
|
||||
<DateTimePickerCustom
|
||||
minimumDate={selectedDate as any}
|
||||
label="Tanggal & Waktu Berakhir"
|
||||
required
|
||||
value={selectedEndDate as any}
|
||||
onChange={(date: any) => {
|
||||
setSelectedEndDate(date as any);
|
||||
}}
|
||||
/>
|
||||
|
||||
<DateTimePickerCustom
|
||||
label="Tanggal & Waktu Mulai"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedDate(date as any);
|
||||
}}
|
||||
value={selectedDate as any}
|
||||
minimumDate={new Date(Date.now())}
|
||||
/>
|
||||
{/* Muncul */}
|
||||
{validateDateRange(selectedDate as any, selectedEndDate as any)
|
||||
.isValid ? (
|
||||
<TextCustom style={{ color: "green" }}>
|
||||
{
|
||||
validateDateRange(
|
||||
selectedDate as any,
|
||||
selectedEndDate as any
|
||||
).error
|
||||
}
|
||||
</TextCustom>
|
||||
) : (
|
||||
<TextCustom style={{ color: "red" }}>
|
||||
{
|
||||
validateDateRange(
|
||||
selectedDate as any,
|
||||
selectedEndDate as any
|
||||
).error
|
||||
}
|
||||
</TextCustom>
|
||||
)}
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
|
||||
<DateTimePickerCustom
|
||||
label="Tanggal & Waktu Berakhir"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedEndDate(date as any);
|
||||
}}
|
||||
value={selectedEndDate as any}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukkan deskripsi event"
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
value={data?.deskripsi}
|
||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukkan deskripsi event"
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
/>
|
||||
|
||||
{buttonSubmit}
|
||||
</StackCustom>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
title="Update"
|
||||
onPress={handlerSubmit}
|
||||
/>
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
@@ -9,12 +10,29 @@ import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { apiEventGetOne } from "@/service/api-client/api-event";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function EventDetailHistory() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [data, setData] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
@@ -32,7 +50,7 @@ export default function EventDetailHistory() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection />
|
||||
<Event_BoxDetailPublishSection data={data} />
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
<DrawerCustom
|
||||
@@ -43,7 +61,7 @@ export default function EventDetailHistory() {
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
|
||||
@@ -1,17 +1,102 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
LoaderCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import {
|
||||
apiEventGetOne,
|
||||
apiEventListOfParticipants,
|
||||
} from "@/service/api-client/api-event";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function EventListOfParticipants() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [startDate, setStartDate] = useState();
|
||||
const [listData, setListData] = useState([]);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
handlerLoadData();
|
||||
}, [id]);
|
||||
|
||||
const handlerLoadData = () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
onLoadData();
|
||||
onLoadList();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setStartDate(response.data.tanggal);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadList = async () => {
|
||||
try {
|
||||
const response = await apiEventListOfParticipants({ id: id as string });
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox key={index} paddingBlock={0}>
|
||||
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${index}`} />
|
||||
</BaseBox>
|
||||
))}
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : listData.length === 0 ? (
|
||||
<TextCustom align="center">Belum ada peserta</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<BaseBox key={index}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={item?.User?.Profile?.imageId}
|
||||
name={item?.User?.username}
|
||||
avatarHref={`/profile/${item?.User?.Profile?.id}`}
|
||||
rightComponent={
|
||||
new Date().getTime() > new Date(startDate as any).getTime() ? (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<BadgeCustom color={item?.isPresent ? "green" : "red"}>
|
||||
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
|
||||
</BadgeCustom>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<BadgeCustom color="gray">-</BadgeCustom>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,73 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
AlertDefaultSystem,
|
||||
ButtonCustom,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Alert } from "react-native";
|
||||
import {
|
||||
apiEventCheckParticipants,
|
||||
apiEventGetOne,
|
||||
apiEventJoin,
|
||||
} from "@/service/api-client/api-event";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function EventDetailPublish() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [isLoadingData, setIsLoadingData] = useState(false);
|
||||
const [isLoadingJoin, setIsLoadingJoin] = useState(false);
|
||||
|
||||
const [data, setData] = useState();
|
||||
const [isParticipant, setIsParticipant] = useState<boolean | null>(null);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
setIsLoadingData(true);
|
||||
const response = await apiEventGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
|
||||
const responseCheckParticipants = await apiEventCheckParticipants({
|
||||
id: id as string,
|
||||
userId: user?.id as string,
|
||||
});
|
||||
|
||||
if (
|
||||
responseCheckParticipants.success &&
|
||||
responseCheckParticipants.data
|
||||
) {
|
||||
setIsParticipant(true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadingData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH ", item.path);
|
||||
@@ -24,15 +75,61 @@ export default function EventDetailPublish() {
|
||||
setOpenDrawer(false);
|
||||
};
|
||||
|
||||
const footerButton = (
|
||||
<ButtonCustom
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
onPress={() => Alert.alert("Anda berhasil join event ini")}
|
||||
>
|
||||
Join
|
||||
</ButtonCustom>
|
||||
);
|
||||
const handlerJoin = async () => {
|
||||
const userId = user?.id;
|
||||
if (!userId) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text2: "Anda belum login",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingJoin(true);
|
||||
const response = await apiEventJoin({
|
||||
id: id as string,
|
||||
userId: userId as string,
|
||||
});
|
||||
if (response.success) {
|
||||
router.navigate(
|
||||
`/(application)/(user)/event/${id}/list-of-participants`
|
||||
);
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Anda berhasil join",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadingJoin(false);
|
||||
}
|
||||
};
|
||||
|
||||
const footerButton = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
disabled={isParticipant as any}
|
||||
isLoading={isLoadingJoin}
|
||||
backgroundColor="green"
|
||||
textColor="white"
|
||||
onPress={() =>
|
||||
AlertDefaultSystem({
|
||||
title: "Join event",
|
||||
message: "Anda yakin ingin join sebagai peserta event ?",
|
||||
textLeft: "Tidak",
|
||||
textRight: "Ya",
|
||||
onPressLeft: () => {},
|
||||
onPressRight: () => handlerJoin(),
|
||||
})
|
||||
}
|
||||
>
|
||||
{isParticipant ? "Anda sudah tergabung" : "Join"}
|
||||
</ButtonCustom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -44,19 +141,25 @@ export default function EventDetailPublish() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Event_BoxDetailPublishSection footerButton={footerButton} />
|
||||
<Spacing />
|
||||
{isLoadingData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<Event_BoxDetailPublishSection
|
||||
data={data}
|
||||
footerButton={footerButton()}
|
||||
/>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={250}
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={menuDrawerPublishEvent({ id: id as string })}
|
||||
columns={4}
|
||||
onPressItem={handlePress}
|
||||
onPressItem={handlePress as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
</>
|
||||
|
||||
@@ -1,19 +1,51 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { masterTypeEvent } from "@/lib/dummy-data/event/master-type-event";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiEventCreate } from "@/service/api-client/api-event";
|
||||
import { apiMasterEventType } from "@/service/api-client/api-master";
|
||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||
import { router } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface EventCreateProps {
|
||||
title?: string;
|
||||
lokasi?: string;
|
||||
deskripsi?: string;
|
||||
eventMaster_TipeAcaraId?: string;
|
||||
tanggal?: string;
|
||||
tanggalSelesai?: string;
|
||||
authorId?: string;
|
||||
}
|
||||
|
||||
export default function EventCreate() {
|
||||
const [data, setData] = useState<EventCreateProps>();
|
||||
const [listTypeEvent, setListTypeEvent] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { user } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
onLoadMasterEventType();
|
||||
}, []);
|
||||
|
||||
const onLoadMasterEventType = async () => {
|
||||
try {
|
||||
const response = await apiMasterEventType();
|
||||
setListTypeEvent(response.data);
|
||||
} catch (error) {
|
||||
console.log("Error onLoadMasterEventType", error);
|
||||
}
|
||||
};
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState<
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
@@ -22,35 +54,76 @@ export default function EventCreate() {
|
||||
Date | DateTimePickerEvent | null
|
||||
>(null);
|
||||
|
||||
const handlerSubmit = () => {
|
||||
const handlerSubmit = async () => {
|
||||
if (
|
||||
!data?.title ||
|
||||
!data?.lokasi ||
|
||||
!data?.deskripsi ||
|
||||
!data?.eventMaster_TipeAcaraId
|
||||
) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Lengkapi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedDate || !selectedEndDate) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Pilih tanggal mulai dan berakhir",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// if (selectedDate) {
|
||||
// console.log("Tanggal yang dipilih:", selectedDate);
|
||||
// console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
|
||||
// // Kirim ke API atau proses lanjutan
|
||||
// } else {
|
||||
// console.log("Tanggal belum dipilih");
|
||||
// }
|
||||
|
||||
// if (selectedEndDate) {
|
||||
// console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
// console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// // Kirim ke API atau proses lanjutan
|
||||
// } else {
|
||||
// console.log("Tanggal berakhir belum dipilih");
|
||||
// }
|
||||
|
||||
try {
|
||||
if (selectedDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal belum dipilih");
|
||||
}
|
||||
setIsLoading(true);
|
||||
|
||||
if (selectedEndDate) {
|
||||
console.log("Tanggal yang dipilih:", selectedEndDate);
|
||||
console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
||||
// Kirim ke API atau proses lanjutan
|
||||
} else {
|
||||
console.log("Tanggal berakhir belum dipilih");
|
||||
}
|
||||
const newData = {
|
||||
...data,
|
||||
tanggal: new Date(selectedDate as any).toISOString(),
|
||||
tanggalSelesai: new Date(selectedEndDate as any).toISOString(),
|
||||
authorId: user?.id,
|
||||
};
|
||||
|
||||
console.log("Data berhasil disimpan", JSON.stringify(newData, null, 2));
|
||||
|
||||
const response = await apiEventCreate(newData);
|
||||
console.log("Response", JSON.stringify(response, null, 2));
|
||||
|
||||
console.log("Data berhasil disimpan");
|
||||
router.navigate("/event/status");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = (
|
||||
<ButtonCustom title="Simpan" onPress={handlerSubmit} />
|
||||
// <BoxButtonOnFooter>
|
||||
// </BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
title="Simpan"
|
||||
onPress={handlerSubmit}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -61,17 +134,27 @@ export default function EventCreate() {
|
||||
placeholder="Masukkan nama event"
|
||||
label="Nama Event"
|
||||
required
|
||||
onChangeText={(value: any) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Tipe Event"
|
||||
placeholder="Pilih tipe event"
|
||||
data={masterTypeEvent}
|
||||
onChange={(value) => console.log(value)}
|
||||
data={listTypeEvent.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data?.eventMaster_TipeAcaraId || ""}
|
||||
onChange={(value: any) =>
|
||||
setData({ ...data, eventMaster_TipeAcaraId: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
label="Lokasi"
|
||||
placeholder="Masukkan lokasi event"
|
||||
required
|
||||
onChangeText={(value: any) => setData({ ...data, lokasi: value })}
|
||||
/>
|
||||
|
||||
<DateTimePickerCustom
|
||||
@@ -84,14 +167,24 @@ export default function EventCreate() {
|
||||
minimumDate={new Date(Date.now())}
|
||||
/>
|
||||
|
||||
<DateTimePickerCustom
|
||||
label="Tanggal & Waktu Berakhir"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedEndDate(date as any);
|
||||
}}
|
||||
value={selectedEndDate as any}
|
||||
/>
|
||||
<StackCustom gap={0}>
|
||||
<DateTimePickerCustom
|
||||
disabled={!selectedDate}
|
||||
label="Tanggal & Waktu Berakhir"
|
||||
required
|
||||
onChange={(date: Date) => {
|
||||
setSelectedEndDate(date as any);
|
||||
}}
|
||||
value={selectedEndDate as any}
|
||||
minimumDate={new Date(selectedDate as any)}
|
||||
/>
|
||||
{!selectedDate && (
|
||||
<TextCustom color="gray" size={"small"}>
|
||||
Note: Pilih tanggal mulai terlebih dahulu
|
||||
</TextCustom>
|
||||
)}
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
@@ -99,6 +192,9 @@ export default function EventCreate() {
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, deskripsi: value })
|
||||
}
|
||||
/>
|
||||
|
||||
{buttonSubmit}
|
||||
|
||||
@@ -1,37 +1,98 @@
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { apiForumGetOne, apiForumUpdate } from "@/service/api-client/api-forum";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [text, setText] = useState("");
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiForumGetOne({ id });
|
||||
|
||||
setText(response.data.diskusi);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerUpdateData = async () => {
|
||||
if (!text) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Harap masukkan diskusi",
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiForumUpdate({
|
||||
id: id as string,
|
||||
data: text,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil diupdate",
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = () => {
|
||||
return (
|
||||
<>
|
||||
{!loadingGetData && (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom isLoading={isLoading} onPress={handlerUpdateData}>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
/>
|
||||
<ViewWrapper footerComponent={buttonFooter()}>
|
||||
{!loadingGetData ? (
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={(value) => {
|
||||
setText(value);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LoaderCustom />
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,86 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertCustom,
|
||||
AvatarCustom,
|
||||
AvatarComp,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
DrawerCustom,
|
||||
FloatingButton,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
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 { useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { apiForumGetAll } from "@/service/api-client/api-forum";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function Forumku() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [listData, setListData] = useState<any | null>(null);
|
||||
const [dataUser, setDataUser] = useState<any | null>(null);
|
||||
const [loadingGetList, setLoadingGetList] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
onLoadDataProfile(id as string);
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadDataProfile = async (id: string) => {
|
||||
try {
|
||||
const response = await apiUser(id);
|
||||
|
||||
setDataUser(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetList(true);
|
||||
const response = await apiForumGetAll({
|
||||
search: "",
|
||||
authorId: id as string,
|
||||
});
|
||||
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetList(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<ViewWrapper
|
||||
floatingButton={
|
||||
user?.id === id && (
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
router.navigate("/(application)/(user)/forum/create")
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
<CenterCustom>
|
||||
<AvatarCustom
|
||||
href={`/(application)/(image)/preview-image/${id}`}
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
href={`/(application)/(image)/preview-image/${dataUser?.Profile?.imageId}`}
|
||||
size="xl"
|
||||
/>
|
||||
</CenterCustom>
|
||||
@@ -37,32 +88,43 @@ export default function Forumku() {
|
||||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom bold truncate>
|
||||
@bagas_banuna
|
||||
@{dataUser?.username || "-"}
|
||||
</TextCustom>
|
||||
<TextCustom>1 postingan</TextCustom>
|
||||
<TextCustom>{listData?.length || "0"} postingan</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||
<ButtonCustom href={`/profile/${id}`}>
|
||||
<ButtonCustom href={`/profile/${dataUser?.Profile?.id}`}>
|
||||
Kunjungi Profile
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
{listDummyDiscussionForum.map((e, i) => (
|
||||
<Forum_BoxDetailSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
isTruncate={true}
|
||||
href={`/forum/${id}`}
|
||||
/>
|
||||
))}
|
||||
{loadingGetList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom> Tidak ada diskusi</TextCustom>
|
||||
) : (
|
||||
<>
|
||||
{listData?.map((item: any, index: number) => (
|
||||
<Forum_BoxDetailSection
|
||||
isRightComponent={false}
|
||||
key={index}
|
||||
data={item}
|
||||
isTruncate={true}
|
||||
href={`/forum/${item.id}`}
|
||||
onSetData={(value) => {
|
||||
setOpenDrawer(value.setOpenDrawer);
|
||||
setStatus(value.setStatus);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
height={"auto"}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
@@ -72,42 +134,9 @@ export default function Forumku() {
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
authorId={id as string}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Komponen Eksternal */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
onLeftPress={() => setAlertStatus(false)}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
onLeftPress={() => setDeleteAlert(false)}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,176 +1,263 @@
|
||||
import {
|
||||
AlertCustom,
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
|
||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
||||
import { listDummyCommentarForum } from "@/screens/Forum/list-data-dummy";
|
||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
||||
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
apiForumCreateComment,
|
||||
apiForumGetComment,
|
||||
apiForumGetOne,
|
||||
apiForumUpdateStatus,
|
||||
} from "@/service/api-client/api-forum";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
interface CommentProps {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
komentar: string;
|
||||
createdAt: Date;
|
||||
authorId: string;
|
||||
Author: {
|
||||
id: string;
|
||||
username: string;
|
||||
Profile: {
|
||||
id: string;
|
||||
imageId: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default function ForumDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
console.log(id);
|
||||
const { user } = useAuth();
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [listComment, setListComment] = useState<CommentProps[] | null>(null);
|
||||
const [isLoadingComment, setLoadingComment] = useState(false);
|
||||
|
||||
// Status
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [text, setText] = useState("");
|
||||
const [authorId, setAuthorId] = useState("");
|
||||
const [dataId, setDataId] = useState("");
|
||||
|
||||
// Comentar
|
||||
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
|
||||
const [alertDeleteCommentar, setAlertDeleteCommentar] = useState(false);
|
||||
const [commentId, setCommentId] = useState("");
|
||||
const [commentAuthorId, setCommentAuthorId] = useState("");
|
||||
|
||||
const dataDummy = {
|
||||
name: "Bagas",
|
||||
status: "Open",
|
||||
date: "14/07/2025",
|
||||
deskripsi:
|
||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae inventore iure pariatur, libero omnis excepturi. Ullam ad officiis deleniti quos esse odit nesciunt, ipsam adipisci cumque aliquam corporis culpa fugit?",
|
||||
jumlahBalas: 2,
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
try {
|
||||
const response = await apiForumGetOne({ id });
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onLoadListComment(id as string);
|
||||
}, [id]);
|
||||
|
||||
const onLoadListComment = async (id: string) => {
|
||||
try {
|
||||
const response = await apiForumGetComment({
|
||||
id: id as string,
|
||||
});
|
||||
setListComment(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Update Status
|
||||
const handlerUpdateStatus = async (value: any) => {
|
||||
try {
|
||||
const response = await apiForumUpdateStatus({
|
||||
id: id as string,
|
||||
data: value,
|
||||
});
|
||||
if (response.success) {
|
||||
setStatus(response.data);
|
||||
setData({
|
||||
...data,
|
||||
ForumMaster_StatusPosting: {
|
||||
status: response.data,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Create Commentar
|
||||
const handlerCreateCommentar = async () => {
|
||||
const newData = {
|
||||
comment: text,
|
||||
authorId: user?.id,
|
||||
};
|
||||
|
||||
try {
|
||||
setLoadingComment(true);
|
||||
const response = await apiForumCreateComment({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setText("");
|
||||
const newComment = {
|
||||
id: response.data.id,
|
||||
isActive: response.data.isActive,
|
||||
komentar: response.data.komentar,
|
||||
createdAt: response.data.createdAt,
|
||||
authorId: response.data.authorId,
|
||||
Author: response.data.Author,
|
||||
};
|
||||
setListComment((prev) => [newComment, ...(prev || [])]);
|
||||
setData({
|
||||
...data,
|
||||
count: data.count + 1,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingComment(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{/* <StackCustom>
|
||||
</StackCustom> */}
|
||||
<Forum_BoxDetailSection
|
||||
data={dataDummy}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
/>
|
||||
{!data && !listComment ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
{/* Box Posting */}
|
||||
<Forum_BoxDetailSection
|
||||
data={data}
|
||||
onSetData={() => {
|
||||
setOpenDrawer(true);
|
||||
setStatus(data.ForumMaster_StatusPosting?.status);
|
||||
setAuthorId(data.Author?.id);
|
||||
setDataId(data.id);
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}
|
||||
/>
|
||||
<ButtonCustom
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Balas
|
||||
</ButtonCustom>
|
||||
{/* Area Commentar */}
|
||||
{data?.ForumMaster_StatusPosting?.status === "Open" && (
|
||||
<>
|
||||
<TextAreaCustom
|
||||
placeholder="Ketik diskusi anda..."
|
||||
maxLength={1000}
|
||||
showCount
|
||||
value={text}
|
||||
onChangeText={setText}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
}}
|
||||
/>
|
||||
<ButtonCustom
|
||||
isLoading={isLoadingComment}
|
||||
style={{
|
||||
alignSelf: "flex-end",
|
||||
}}
|
||||
onPress={() => {
|
||||
handlerCreateCommentar();
|
||||
}}
|
||||
>
|
||||
Balas
|
||||
</ButtonCustom>
|
||||
</>
|
||||
)}
|
||||
<Spacing height={40} />
|
||||
|
||||
<Spacing height={40} />
|
||||
|
||||
{listDummyCommentarForum.map((e, i) => (
|
||||
<Forum_CommentarBoxSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawerCommentar}
|
||||
/>
|
||||
))}
|
||||
{/* List Commentar */}
|
||||
{_.isEmpty(listComment) ? (
|
||||
<TextCustom align="center" color="gray" size={"small"}>
|
||||
Tidak ada komentar
|
||||
</TextCustom>
|
||||
) : (
|
||||
<TextCustom color="gray">Komentar :</TextCustom>
|
||||
)}
|
||||
<Spacing height={5} />
|
||||
{listComment?.map((item: any, index: number) => (
|
||||
<Forum_CommentarBoxSection
|
||||
key={index}
|
||||
data={item}
|
||||
onSetData={(value) => {
|
||||
setCommentId(value.setCommentId);
|
||||
setOpenDrawerCommentar(value.setOpenDrawer);
|
||||
setCommentAuthorId(value.setCommentAuthorId);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Posting Drawer */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
height={"auto"}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id as string}
|
||||
id={dataId}
|
||||
status={status}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
authorId={authorId}
|
||||
handlerUpdateStatus={(value: any) => {
|
||||
handlerUpdateStatus(value);
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Status */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
|
||||
{/* Commentar */}
|
||||
{/* Commentar Drawer */}
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
height={"auto"}
|
||||
isVisible={openDrawerCommentar}
|
||||
closeDrawer={() => setOpenDrawerCommentar(false)}
|
||||
>
|
||||
<Forum_MenuDrawerCommentar
|
||||
id={id as string}
|
||||
id={commentId as string}
|
||||
commentId={commentId}
|
||||
commentAuthorId={commentAuthorId}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawerCommentar(false);
|
||||
}}
|
||||
setShowDeleteAlert={setAlertDeleteCommentar}
|
||||
listComment={listComment}
|
||||
setListComment={setListComment}
|
||||
countComment={data?.count}
|
||||
setCountComment={(val: any) => {
|
||||
setData((prev: any) => ({
|
||||
...prev,
|
||||
count: val,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Delete Commentar */}
|
||||
<AlertCustom
|
||||
isVisible={alertDeleteCommentar}
|
||||
title="Hapus Komentar"
|
||||
message="Apakah Anda yakin ingin menghapus komentar ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawerCommentar(false);
|
||||
setAlertDeleteCommentar(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawerCommentar(false);
|
||||
setAlertDeleteCommentar(false);
|
||||
console.log("Hapus commentar");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,27 +5,69 @@ import {
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { router } from "expo-router";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiForumCreateReportCommentar } from "@/service/api-client/api-master";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumOtherReportCommentar() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
const handlerSubmitReport = async () => {
|
||||
const newData = {
|
||||
authorId: user?.id,
|
||||
description: value,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await apiForumCreateReportCommentar({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Laporan berhasil dikirim",
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Laporan gagal dikirim",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
disabled={!value}
|
||||
backgroundColor={MainColor.red}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Report lainnya");
|
||||
router.back();
|
||||
handlerSubmitReport();
|
||||
}}
|
||||
>
|
||||
Report
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={handleSubmit}>
|
||||
<TextAreaCustom placeholder="Laporkan Komentar" />
|
||||
<TextAreaCustom
|
||||
placeholder="Laporkan Komentar"
|
||||
value={value}
|
||||
onChangeText={setValue}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -5,17 +5,54 @@ import {
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { router } from "expo-router";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiForumCreateReportPosting } from "@/service/api-client/api-master";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumOtherReportPosting() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
const handlerSubmitReport = async () => {
|
||||
const newData = {
|
||||
authorId: user?.id,
|
||||
description: value,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await apiForumCreateReportPosting({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Laporan berhasil dikirim",
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Laporan gagal dikirim",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
disabled={!value}
|
||||
backgroundColor={MainColor.red}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Report lainnya");
|
||||
router.back();
|
||||
handlerSubmitReport();
|
||||
}}
|
||||
>
|
||||
Report
|
||||
@@ -25,7 +62,11 @@ export default function ForumOtherReportPosting() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={handleSubmit}>
|
||||
<TextAreaCustom placeholder="Laporkan Diskusi" />
|
||||
<TextAreaCustom
|
||||
placeholder="Laporkan Diskusi"
|
||||
value={value}
|
||||
onChangeText={setValue}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,41 +1,105 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||
import { router } from "expo-router";
|
||||
import { apiForumCreateReportCommentar, apiMasterForumReportList } from "@/service/api-client/api-master";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState, useEffect } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumReportCommentar() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [selectReport, setSelectReport] = useState<string>("");
|
||||
const [listMaster, setListMaster] = useState<any[] | null>(null);
|
||||
const [isLoadingList, setIsLoadingList] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadListMaster();
|
||||
}, []);
|
||||
|
||||
const onLoadListMaster = async () => {
|
||||
try {
|
||||
setIsLoadingList(true);
|
||||
const response = await apiMasterForumReportList();
|
||||
|
||||
setListMaster(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadingList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerReport = async () => {
|
||||
const newData = {
|
||||
authorId: user?.id,
|
||||
categoryId: selectReport,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await apiForumCreateReportCommentar({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Laporan berhasil dikirim",
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Laporan gagal dikirim",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<StackCustom>
|
||||
<Forum_ReportListSection />
|
||||
<ButtonCustom
|
||||
backgroundColor={MainColor.red}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Report");
|
||||
router.back();
|
||||
}}
|
||||
>
|
||||
Report
|
||||
</ButtonCustom>
|
||||
<ButtonCustom
|
||||
backgroundColor={AccentColor.blue}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Lainnya");
|
||||
router.replace("/forum/[id]/other-report-commentar");
|
||||
}}
|
||||
>
|
||||
Lainnya
|
||||
</ButtonCustom>
|
||||
<Spacing/>
|
||||
</StackCustom>
|
||||
{isLoadingList ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
<Forum_ReportListSection
|
||||
listMaster={listMaster}
|
||||
selectReport={selectReport}
|
||||
setSelectReport={setSelectReport}
|
||||
/>
|
||||
<ButtonCustom
|
||||
disabled={!selectReport}
|
||||
backgroundColor={MainColor.red}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
handlerReport();
|
||||
}}
|
||||
>
|
||||
Report
|
||||
</ButtonCustom>
|
||||
<ButtonCustom
|
||||
backgroundColor={AccentColor.blue}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
router.replace(`/forum/${id}/other-report-commentar`);
|
||||
}}
|
||||
>
|
||||
Lainnya
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,103 @@
|
||||
import { ViewWrapper, StackCustom, ButtonCustom, Spacing } from "@/components";
|
||||
import { MainColor, AccentColor } from "@/constants/color-palet";
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||
import { router } from "expo-router";
|
||||
import {
|
||||
apiForumCreateReportPosting,
|
||||
apiMasterForumReportList,
|
||||
} from "@/service/api-client/api-master";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumReportPosting() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [selectReport, setSelectReport] = useState<string>("");
|
||||
const [listMaster, setListMaster] = useState<any[] | null>(null);
|
||||
const [isLoadingList, setIsLoadingList] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadListMaster();
|
||||
}, []);
|
||||
|
||||
const onLoadListMaster = async () => {
|
||||
try {
|
||||
setIsLoadingList(true);
|
||||
const response = await apiMasterForumReportList();
|
||||
|
||||
setListMaster(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadingList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerReport = async () => {
|
||||
const newData = {
|
||||
authorId: user?.id,
|
||||
categoryId: selectReport,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await apiForumCreateReportPosting({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
});
|
||||
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Laporan berhasil dikirim",
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: "Laporan gagal dikirim",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{isLoadingList ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
<Forum_ReportListSection />
|
||||
<Forum_ReportListSection
|
||||
listMaster={listMaster}
|
||||
selectReport={selectReport}
|
||||
setSelectReport={setSelectReport}
|
||||
/>
|
||||
|
||||
<ButtonCustom
|
||||
disabled={!selectReport}
|
||||
backgroundColor={MainColor.red}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Report");
|
||||
router.back();
|
||||
AlertDefaultSystem({
|
||||
title: "Laporan Posting",
|
||||
message: "Apakah anda yakin ingin melaporkan postingan ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Laporkan",
|
||||
onPressRight: () => {
|
||||
handlerReport();
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Report
|
||||
@@ -23,15 +106,15 @@ export default function ForumReportPosting() {
|
||||
backgroundColor={AccentColor.blue}
|
||||
textColor={MainColor.white}
|
||||
onPress={() => {
|
||||
console.log("Lainnya");
|
||||
router.replace("/forum/[id]/other-report-posting");
|
||||
router.replace(`/forum/${id}/other-report-posting`);
|
||||
}}
|
||||
>
|
||||
Lainnya
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,18 +4,47 @@ import {
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiForumCreate } from "@/service/api-client/api-forum";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function ForumCreate() {
|
||||
const { user } = useAuth();
|
||||
const [text, setText] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
const newData = {
|
||||
diskusi: text,
|
||||
authorId: user?.id,
|
||||
};
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiForumCreate({ data: newData });
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Posting berhasil",
|
||||
});
|
||||
setText("");
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
console.log("Posting", text);
|
||||
router.back();
|
||||
handlerSubmit();
|
||||
}}
|
||||
>
|
||||
Posting
|
||||
|
||||
@@ -1,25 +1,58 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertCustom,
|
||||
AvatarCustom,
|
||||
AvatarComp,
|
||||
BackButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import FloatingButton from "@/components/Button/FloatingButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
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 { router, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { apiForumGetAll } from "@/service/api-client/api-forum";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { router, Stack, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function Forum() {
|
||||
const id = "test-id-forum";
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [alertStatus, setAlertStatus] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const { user } = useAuth();
|
||||
const [dataUser, setDataUser] = useState<any>();
|
||||
const [listData, setListData] = useState<any[]>();
|
||||
const [loadingGetList, setLoadingGetList] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const [dataId, setDataId] = useState("");
|
||||
const [authorId, setAuthorId] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
onLoadDataProfile(user?.id as string);
|
||||
}, [user?.id, search])
|
||||
);
|
||||
|
||||
const onLoadDataProfile = async (id: string) => {
|
||||
const response = await apiUser(id);
|
||||
setDataUser(response.data);
|
||||
};
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetList(true);
|
||||
const response = await apiForumGetAll({ search: search });
|
||||
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetList(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -27,12 +60,23 @@ export default function Forum() {
|
||||
options={{
|
||||
title: "Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <AvatarCustom href={`/forum/${id}/forumku`} />,
|
||||
headerRight: () => (
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<ViewWrapper
|
||||
headerComponent={<SearchInput placeholder="Cari topik diskusi" />}
|
||||
headerComponent={
|
||||
<SearchInput
|
||||
placeholder="Cari topik diskusi"
|
||||
onChangeText={(e) => setSearch(e)}
|
||||
/>
|
||||
}
|
||||
floatingButton={
|
||||
<FloatingButton
|
||||
onPress={() =>
|
||||
@@ -41,73 +85,45 @@ export default function Forum() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{listDummyDiscussionForum.map((e, i) => (
|
||||
<Forum_BoxDetailSection
|
||||
key={i}
|
||||
data={e}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
setStatus={setStatus}
|
||||
isTruncate={true}
|
||||
href={`/forum/${id}`}
|
||||
/>
|
||||
))}
|
||||
{loadingGetList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center" color="gray">
|
||||
Tidak ada diskusi
|
||||
</TextCustom>
|
||||
) : (
|
||||
listData?.map((e: any, i: number) => (
|
||||
<Forum_BoxDetailSection
|
||||
key={i}
|
||||
data={e}
|
||||
onSetData={() => {
|
||||
setDataId(e.id);
|
||||
setOpenDrawer(true);
|
||||
setStatus(e.ForumMaster_StatusPosting?.status);
|
||||
setAuthorId(e.Author?.id);
|
||||
}}
|
||||
isTruncate={true}
|
||||
href={`/forum/${e.id}`}
|
||||
isRightComponent={false}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
height={350}
|
||||
height={"auto"}
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
>
|
||||
<Forum_MenuDrawerBerandaSection
|
||||
id={id}
|
||||
id={dataId}
|
||||
authorId={authorId}
|
||||
status={status}
|
||||
setIsDrawerOpen={() => {
|
||||
setOpenDrawer(false);
|
||||
}}
|
||||
setShowDeleteAlert={setDeleteAlert}
|
||||
setShowAlertStatus={setAlertStatus}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Status */}
|
||||
<AlertCustom
|
||||
isVisible={alertStatus}
|
||||
title="Ubah Status Forum"
|
||||
message="Apakah Anda yakin ingin mengubah status forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setAlertStatus(false);
|
||||
console.log("Ubah status forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Ubah"
|
||||
colorRight={MainColor.green}
|
||||
/>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
title="Hapus Forum"
|
||||
message="Apakah Anda yakin ingin menghapus forum ini?"
|
||||
onLeftPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Batal");
|
||||
}}
|
||||
onRightPress={() => {
|
||||
setOpenDrawer(false);
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus forum");
|
||||
}}
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { StackCustom, ViewWrapper } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -8,29 +9,40 @@ import TabSection from "@/screens/Home/tabSection";
|
||||
import { tabsHome } from "@/screens/Home/tabsList";
|
||||
import Home_FeatureSection from "@/screens/Home/topFeatureSection";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { apiVersion } from "@/service/api-config";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Redirect, router, Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Application() {
|
||||
const { user } = useAuth();
|
||||
const [data, setData] = useState<any>({});
|
||||
const { token, user } = useAuth();
|
||||
|
||||
const [data, setData] = useState<any>();
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
checkVersion();
|
||||
}, []);
|
||||
|
||||
async function onLoadData() {
|
||||
const response = await apiUser(user?.id as string);
|
||||
console.log("User >>", JSON.stringify(response.data, null, 2));
|
||||
console.log("Response profile >>", JSON.stringify(response?.data?.Profile, null, 2));
|
||||
|
||||
setData(response.data);
|
||||
}
|
||||
|
||||
const checkVersion = async () => {
|
||||
const response = await apiVersion();
|
||||
console.log("Version >>", JSON.stringify(response.data, null, 2));
|
||||
};
|
||||
|
||||
if (data && data?.active === false) {
|
||||
console.log("User is not active");
|
||||
return <Redirect href={`/waiting-room`} />;
|
||||
}
|
||||
|
||||
if (data && data?.Profile === null) {
|
||||
console.log("Profile is null");
|
||||
return <Redirect href={`/profile/create`} />;
|
||||
}
|
||||
|
||||
@@ -38,7 +50,7 @@ export default function Application() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "HIPMI",
|
||||
title: `HIPMI`,
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="search"
|
||||
|
||||
@@ -1,13 +1,48 @@
|
||||
import { ScrollableCustom, ViewWrapper } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
LoaderCustom,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import Investment_StatusBox from "@/screens/Invesment/StatusBox";
|
||||
import { useState } from "react";
|
||||
import { apiInvestmentGetByStatus } from "@/service/api-client/api-investment";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function InvestmentPortofolio() {
|
||||
const { user } = useAuth();
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const [listData, setListData] = useState<any[]>([]);
|
||||
const [loadingList, setLoadingList] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [user?.id, activeCategory])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingList(true);
|
||||
const response = await apiInvestmentGetByStatus({
|
||||
authorId: user?.id as string,
|
||||
status: activeCategory as string,
|
||||
});
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
@@ -26,14 +61,20 @@ export default function InvestmentPortofolio() {
|
||||
);
|
||||
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`}
|
||||
/>
|
||||
))}
|
||||
{loadingList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<Investment_StatusBox
|
||||
key={index}
|
||||
data={item}
|
||||
status={activeCategory as string}
|
||||
href={`/investment/${item.id}/${activeCategory}/detail`}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
@@ -12,16 +13,42 @@ 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 { apiInvestmentGetById } from "@/service/api-client/api-investment";
|
||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function InvestmentDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const [data, setData] = useState<any>(null);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id, status])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiInvestmentGetById({
|
||||
id: id as string,
|
||||
});
|
||||
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
router.navigate(item.path as any);
|
||||
@@ -36,7 +63,8 @@ export default function InvestmentDetailStatus() {
|
||||
|
||||
const bottomSection = (
|
||||
<Invesment_ComponentBoxOnBottomDetail
|
||||
id={id as string}
|
||||
id={data?.id}
|
||||
prospectusId={data?.prospektusFileId}
|
||||
status={status as string}
|
||||
/>
|
||||
);
|
||||
@@ -63,6 +91,7 @@ export default function InvestmentDetailStatus() {
|
||||
<ViewWrapper>
|
||||
<Invesment_DetailDataPublishSection
|
||||
status={status as string}
|
||||
data={data}
|
||||
bottomSection={bottomSection}
|
||||
buttonSection={buttonSection}
|
||||
/>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
@@ -5,38 +6,149 @@ import {
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import {
|
||||
apiInvestmentGetById,
|
||||
apiInvestmentUpdateData,
|
||||
} from "@/service/api-client/api-investment";
|
||||
import { deleteFileService, uploadFileService } from "@/service/upload-service";
|
||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function InvestmentEditProspectus() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [loadingGet, setLoadingGet] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [pdf, setPdf] = useState<IFileData | null>(null);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGet(true);
|
||||
const response = await apiInvestmentGetById({
|
||||
id: id as string,
|
||||
});
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGet(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmitUpdate = async () => {
|
||||
const prevProspectusFileId = data?.prospektusFileId;
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const responseUploadImage = await uploadFileService({
|
||||
imageUri: pdf?.uri as any,
|
||||
dirId: DIRECTORY_ID.investasi_prospektus,
|
||||
});
|
||||
|
||||
if (!responseUploadImage.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const prospektusFileId = responseUploadImage.data.id;
|
||||
|
||||
const responseUpdate = await apiInvestmentUpdateData({
|
||||
id: id as string,
|
||||
data: prospektusFileId,
|
||||
category: "prospectus",
|
||||
});
|
||||
|
||||
if (responseUpdate.success) {
|
||||
const deletePrevImage = await deleteFileService({
|
||||
id: prevProspectusFileId as any,
|
||||
});
|
||||
|
||||
if (!deletePrevImage.success) {
|
||||
console.log("[ERROR DELETE PREV IMAGE]", deletePrevImage.message);
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Data berhasil diupdate",
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: responseUpdate.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
<ButtonCustom
|
||||
disabled={pdf === null}
|
||||
isLoading={isLoading}
|
||||
onPress={handleSubmitUpdate}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<ViewWrapper footerComponent={!loadingGet && 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}
|
||||
/>
|
||||
{loadingGet ? (
|
||||
<LoaderCustom />
|
||||
) : pdf ? (
|
||||
<TextCustom truncate>{pdf.name}</TextCustom>
|
||||
) : (
|
||||
<TextCustom truncate>
|
||||
{_.snakeCase(data?.title || "").replace(/_/g, "-")}.pdf
|
||||
</TextCustom>
|
||||
)}
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
disabled={loadingGet}
|
||||
icon="upload"
|
||||
onPress={() => router.push("/(application)/(image)/take-picture/123")}
|
||||
onPress={() => {
|
||||
pickFile({
|
||||
allowedType: "pdf",
|
||||
setPdfUri(file: any) {
|
||||
setPdf({
|
||||
uri: file.uri,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
});
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
@@ -1,40 +1,224 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
LoaderCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper
|
||||
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";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import {
|
||||
apiInvestmentGetById,
|
||||
apiInvestmentUpdateData,
|
||||
} from "@/service/api-client/api-investment";
|
||||
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
||||
import {
|
||||
deleteFileService,
|
||||
uploadFileService,
|
||||
} from "@/service/upload-service";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import pickFile from "@/utils/pickFile";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface IInvestment {
|
||||
title?: string;
|
||||
targetDana?: string;
|
||||
hargaLembar?: string;
|
||||
totalLembar?: string;
|
||||
roi?: string;
|
||||
masterPencarianInvestorId?: string;
|
||||
masterPeriodeDevidenId?: string;
|
||||
masterPembagianDevidenId?: string;
|
||||
authorId?: string;
|
||||
imageId?: string;
|
||||
prospektusFileId?: string;
|
||||
}
|
||||
|
||||
export default function InvestmentEdit() {
|
||||
const [data, setData] = useState({
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<IInvestment>({
|
||||
title: "",
|
||||
targetDana: 0,
|
||||
hargaPerLembar: 0,
|
||||
totalLembar: 0,
|
||||
rasioKeuntungan: 0,
|
||||
pencarianInvestor: "",
|
||||
periodeDeviden: "",
|
||||
pembagianDeviden: "",
|
||||
targetDana: "",
|
||||
hargaLembar: "",
|
||||
totalLembar: "",
|
||||
roi: "",
|
||||
masterPencarianInvestorId: "",
|
||||
masterPeriodeDevidenId: "",
|
||||
masterPembagianDevidenId: "",
|
||||
authorId: "",
|
||||
imageId: "",
|
||||
prospektusFileId: "",
|
||||
});
|
||||
|
||||
const [image, setImage] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [loadingMaster, setLoadingMaster] = useState(false);
|
||||
const [listPencarianInvestor, setListPencarianInvestor] = useState<any[]>([]);
|
||||
const [listPeriodeDeviden, setListPeriodeDeviden] = useState<any[]>([]);
|
||||
const [listPembagianDeviden, setListPembagianDeviden] = useState<any[]>([]);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadMaster();
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
setLoadingMaster(true);
|
||||
const response = await apiMasterInvestment({ category: "" });
|
||||
|
||||
setListPencarianInvestor(response.data.pencarianInvestor);
|
||||
setListPeriodeDeviden(response.data.periodeDeviden);
|
||||
setListPembagianDeviden(response.data.pembagianDeviden);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingMaster(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiInvestmentGetById({
|
||||
id: id as string,
|
||||
});
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const displayTargetDana = formatCurrencyDisplay(data?.targetDana);
|
||||
const displayHargaPerLembar = formatCurrencyDisplay(data?.hargaLembar);
|
||||
const displayTotalLembar = formatCurrencyDisplay(
|
||||
Number(data?.targetDana) / Number(data?.hargaLembar)
|
||||
);
|
||||
|
||||
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||
const numeric = text.replace(/\D/g, "");
|
||||
setData((prev) => ({ ...prev, [field]: numeric }));
|
||||
};
|
||||
|
||||
const validateData = () => {
|
||||
if (
|
||||
!data.title ||
|
||||
!data.targetDana ||
|
||||
!data.hargaLembar ||
|
||||
!data.totalLembar ||
|
||||
!data.roi ||
|
||||
!data.masterPencarianInvestorId ||
|
||||
!data.masterPeriodeDevidenId ||
|
||||
!data.masterPembagianDevidenId
|
||||
) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Harap isi semua data",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmitUpdate = async () => {
|
||||
let newData = {
|
||||
...data,
|
||||
};
|
||||
|
||||
if (!validateData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
if (image) {
|
||||
const responseUploadImage = await uploadFileService({
|
||||
imageUri: image,
|
||||
dirId: DIRECTORY_ID.investasi_image,
|
||||
});
|
||||
|
||||
if (!responseUploadImage.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const deletePrevImage = await deleteFileService({
|
||||
id: data?.imageId as any,
|
||||
});
|
||||
|
||||
if (!deletePrevImage.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal menghapus gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
newData = {
|
||||
...newData,
|
||||
imageId: responseUploadImage.data.id,
|
||||
};
|
||||
}
|
||||
|
||||
const responseUpdate = await apiInvestmentUpdateData({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
category: "data"
|
||||
});
|
||||
|
||||
if (responseUpdate.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Data berhasil diupdate",
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: responseUpdate.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
||||
<LandscapeFrameUploaded />
|
||||
<LandscapeFrameUploaded
|
||||
image={
|
||||
image ? image : API_STRORAGE.GET({ fileId: data?.imageId as any })
|
||||
}
|
||||
/>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
onPress={() => {
|
||||
pickFile({
|
||||
setImageUri: ({ uri }) => {
|
||||
setImage(uri);
|
||||
},
|
||||
allowedType: "image",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
@@ -47,7 +231,7 @@ export default function InvestmentEdit() {
|
||||
required
|
||||
placeholder="Judul"
|
||||
label="Judul"
|
||||
value={data.title}
|
||||
value={data?.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
@@ -57,22 +241,8 @@ export default function InvestmentEdit() {
|
||||
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()}
|
||||
onChangeText={handleChangeCurrency("targetDana")}
|
||||
value={displayTargetDana}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
@@ -81,10 +251,8 @@ export default function InvestmentEdit() {
|
||||
placeholder="0"
|
||||
label="Harga Per Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
onChangeText={handleChangeCurrency("hargaLembar")}
|
||||
value={displayHargaPerLembar}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
@@ -92,10 +260,8 @@ export default function InvestmentEdit() {
|
||||
placeholder="0"
|
||||
label="Total Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, totalLembar: Number(value) })
|
||||
}
|
||||
value={data.totalLembar === 0 ? "" : data.totalLembar.toString()}
|
||||
onChangeText={(value) => setData({ ...data, totalLembar: value })}
|
||||
value={displayTotalLembar}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
@@ -104,57 +270,78 @@ export default function InvestmentEdit() {
|
||||
label="Rasio Keuntungan / ROI %"
|
||||
placeholder="0"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, rasioKeuntungan: Number(value) })
|
||||
}
|
||||
value={
|
||||
data.rasioKeuntungan === 0 ? "" : data.rasioKeuntungan.toString()
|
||||
}
|
||||
onChangeText={(value) => setData({ ...data, roi: value })}
|
||||
value={data?.roi === "" ? "" : data?.roi}
|
||||
/>
|
||||
|
||||
<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}
|
||||
/>
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pencarian Investor"
|
||||
data={
|
||||
_.isEmpty(listPencarianInvestor)
|
||||
? []
|
||||
: listPencarianInvestor.map((item) => ({
|
||||
label: item.name + `${" "}hari`,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, masterPencarianInvestorId: value as any })
|
||||
}
|
||||
value={data.masterPencarianInvestorId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<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}
|
||||
/>
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Periode Deviden"
|
||||
data={
|
||||
_.isEmpty(listPeriodeDeviden)
|
||||
? []
|
||||
: listPeriodeDeviden.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, masterPeriodeDevidenId: value as any })
|
||||
}
|
||||
value={data.masterPeriodeDevidenId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Pembagian Deviden"
|
||||
data={
|
||||
_.isEmpty(listPembagianDeviden)
|
||||
? []
|
||||
: listPembagianDeviden.map((item) => ({
|
||||
label: item.name + `${" "}bulan`,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, masterPembagianDevidenId: value as any })
|
||||
}
|
||||
value={data.masterPembagianDevidenId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<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")}>
|
||||
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
|
||||
@@ -1,76 +1,236 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
LoaderCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
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 DIRECTORY_ID from "@/constants/directory-id";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiInvestmentCreate } from "@/service/api-client/api-investment";
|
||||
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function InvestmentCreate() {
|
||||
const { user } = useAuth();
|
||||
const [data, setData] = useState({
|
||||
title: "",
|
||||
targetDana: 0,
|
||||
hargaPerLembar: 0,
|
||||
totalLembar: 0,
|
||||
rasioKeuntungan: 0,
|
||||
targetDana: "",
|
||||
hargaPerLembar: "",
|
||||
totalLembar: "",
|
||||
rasioKeuntungan: "",
|
||||
pencarianInvestor: "",
|
||||
periodeDeviden: "",
|
||||
pembagianDeviden: "",
|
||||
authorId: "",
|
||||
imageId: "",
|
||||
prospektusFileId: "",
|
||||
});
|
||||
const [image, setImage] = useState<string | null>(null);
|
||||
const [pdf, setPdf] = useState<IFileData | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// const [coba, setCoba] = useState("");
|
||||
const [loadingMaster, setLoadingMaster] = useState(false);
|
||||
const [listPencarianInvestor, setListPencarianInvestor] = useState<any[]>([]);
|
||||
const [listPeriodeDeviden, setListPeriodeDeviden] = useState<any[]>([]);
|
||||
const [listPembagianDeviden, setListPembagianDeviden] = useState<any[]>([]);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadMaster();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
setLoadingMaster(true);
|
||||
const response = await apiMasterInvestment({ category: "" });
|
||||
|
||||
setListPencarianInvestor(response.data.pencarianInvestor);
|
||||
setListPeriodeDeviden(response.data.periodeDeviden);
|
||||
setListPembagianDeviden(response.data.pembagianDeviden);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingMaster(false);
|
||||
}
|
||||
};
|
||||
|
||||
const displayTargetDana = formatCurrencyDisplay(data.targetDana);
|
||||
const displayHargaPerLembar = formatCurrencyDisplay(data.hargaPerLembar);
|
||||
const displayTotalLembar = formatCurrencyDisplay(
|
||||
Number(data.targetDana) / Number(data.hargaPerLembar)
|
||||
);
|
||||
|
||||
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||
const numeric = text.replace(/\D/g, "");
|
||||
setData((prev) => ({ ...prev, [field]: numeric }));
|
||||
};
|
||||
|
||||
const validateData = () => {
|
||||
if (
|
||||
!data.title ||
|
||||
!data.targetDana ||
|
||||
!data.hargaPerLembar ||
|
||||
!data.rasioKeuntungan ||
|
||||
!data.pencarianInvestor ||
|
||||
!data.periodeDeviden ||
|
||||
!data.pembagianDeviden
|
||||
) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Harap isi semua data",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!validateData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!image || !pdf) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Harap upload gambar dan file PDF",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const responseUploadImage = await uploadFileService({
|
||||
imageUri: image,
|
||||
dirId: DIRECTORY_ID.investasi_image,
|
||||
});
|
||||
|
||||
if (!responseUploadImage.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah gambar",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const imageId = responseUploadImage.data.id;
|
||||
const responseUploadPdf = await uploadFileService({
|
||||
imageUri: pdf.uri,
|
||||
dirId: DIRECTORY_ID.investasi_prospektus,
|
||||
});
|
||||
|
||||
if (!responseUploadPdf.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mengunggah file PDF",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const pdfId = responseUploadPdf.data.id;
|
||||
const newData = {
|
||||
title: data.title,
|
||||
targetDana: data.targetDana,
|
||||
hargaLembar: data.hargaPerLembar,
|
||||
totalLembar: displayTotalLembar,
|
||||
roi: data.rasioKeuntungan,
|
||||
masterPencarianInvestorId: data.pencarianInvestor,
|
||||
masterPembagianDevidenId: data.pembagianDeviden,
|
||||
masterPeriodeDevidenId: data.periodeDeviden,
|
||||
authorId: user?.id,
|
||||
imageId: imageId,
|
||||
prospektusFileId: pdfId,
|
||||
};
|
||||
|
||||
const response = await apiInvestmentCreate({ data: newData });
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil",
|
||||
text2: response.message,
|
||||
});
|
||||
router.replace("/investment/portofolio");
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 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 />
|
||||
<LandscapeFrameUploaded image={image as string} />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
onPress={() => {
|
||||
pickFile({
|
||||
setImageUri: ({ uri }) => {
|
||||
setImage(uri);
|
||||
},
|
||||
allowedType: "image",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepannya." />
|
||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepannya. Gunakan format PDF." />
|
||||
|
||||
<BaseBox>
|
||||
<CenterCustom>
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
{pdf ? (
|
||||
<TextCustom>{pdf.name}</TextCustom>
|
||||
) : (
|
||||
<FontAwesome5
|
||||
name="file-pdf"
|
||||
size={30}
|
||||
color={MainColor.disabled}
|
||||
/>
|
||||
)}
|
||||
</CenterCustom>
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.push("/take-picture/1")}
|
||||
onPress={() => {
|
||||
pickFile({
|
||||
setPdfUri: ({ uri, name, size }) => {
|
||||
|
||||
setPdf({ uri, name, size });
|
||||
},
|
||||
allowedType: "pdf",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload File
|
||||
</ButtonCenteredOnly>
|
||||
@@ -90,22 +250,8 @@ export default function InvestmentCreate() {
|
||||
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()}
|
||||
onChangeText={handleChangeCurrency("targetDana")}
|
||||
value={displayTargetDana}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
@@ -114,22 +260,24 @@ export default function InvestmentCreate() {
|
||||
placeholder="0"
|
||||
label="Harga Per Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, targetDana: Number(value) })
|
||||
}
|
||||
value={data.targetDana === 0 ? "" : data.targetDana.toString()}
|
||||
onChangeText={handleChangeCurrency("hargaPerLembar")}
|
||||
value={displayHargaPerLembar}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="0"
|
||||
label="Total Lembar"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, totalLembar: Number(value) })
|
||||
}
|
||||
value={data.totalLembar === 0 ? "" : data.totalLembar.toString()}
|
||||
/>
|
||||
<StackCustom gap={0}>
|
||||
<TextInputCustom
|
||||
required
|
||||
placeholder="0"
|
||||
label="Total Lembar"
|
||||
keyboardType="numeric"
|
||||
// onChangeText={handleChangeCurrency("totalLembar")}
|
||||
value={displayTotalLembar}
|
||||
/>
|
||||
<TextCustom size={"small"} color="gray">
|
||||
*Total lembar dihitung dari, Target Dana / Harga Perlembar
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
@@ -137,57 +285,80 @@ export default function InvestmentCreate() {
|
||||
label="Rasio Keuntungan / ROI %"
|
||||
placeholder="0"
|
||||
keyboardType="numeric"
|
||||
onChangeText={(value) =>
|
||||
setData({ ...data, rasioKeuntungan: Number(value) })
|
||||
}
|
||||
onChangeText={(value) => setData({ ...data, rasioKeuntungan: value })}
|
||||
value={
|
||||
data.rasioKeuntungan === 0 ? "" : data.rasioKeuntungan.toString()
|
||||
data.rasioKeuntungan === "" ? "" : 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}
|
||||
/>
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pencarian Investor"
|
||||
data={
|
||||
_.isEmpty(listPencarianInvestor)
|
||||
? []
|
||||
: listPencarianInvestor.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}
|
||||
/>
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Periode Deviden"
|
||||
data={
|
||||
_.isEmpty(listPeriodeDeviden)
|
||||
? []
|
||||
: listPeriodeDeviden.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, periodeDeviden: value as any })
|
||||
}
|
||||
value={data.periodeDeviden}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loadingMaster ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<SelectCustom
|
||||
required
|
||||
placeholder="Pilih batas waktu"
|
||||
label="Pilih Pembagian Deviden"
|
||||
data={
|
||||
_.isEmpty(listPembagianDeviden)
|
||||
? []
|
||||
: listPembagianDeviden.map((item) => ({
|
||||
label: item.name + `${" "}bulan`,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
onChange={(value) =>
|
||||
setData({ ...data, pembagianDeviden: value as any })
|
||||
}
|
||||
value={data.pembagianDeviden}
|
||||
/>
|
||||
)}
|
||||
|
||||
<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")}>
|
||||
<ButtonCustom isLoading={isLoading} onPress={() => handleSubmit()}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</StackCustom>
|
||||
|
||||
@@ -1,16 +1,57 @@
|
||||
import { BaseBox, TextCustom, ViewWrapper } from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { BaseBox, LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiJobGetAll } from "@/service/api-client/api-job";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function JobArchive() {
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any[]>([]);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [user?.id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiJobGetAll({
|
||||
category: "archive",
|
||||
authorId: user?.id,
|
||||
});
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
))}
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Anda tidak memiliki arsip</TextCustom>
|
||||
) : (
|
||||
listData.map((item, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/job/${item.id}/archive`}
|
||||
>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,83 @@
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BoxWithHeaderSection,
|
||||
FloatingButton,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { router } from "expo-router";
|
||||
import { apiJobGetAll } from "@/service/api-client/api-job";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function JobBeranda() {
|
||||
const [listData, setListData] = useState<any[]>([]);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(search);
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async (search: string) => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiJobGetAll({ search, category: "beranda" });
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = (search: string) => {
|
||||
setSearch(search);
|
||||
onLoadData(search);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/job/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari pekerjaan" />}
|
||||
headerComponent={
|
||||
<SearchInput placeholder="Cari pekerjaan" onChangeText={handleSearch} />
|
||||
}
|
||||
>
|
||||
{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>
|
||||
))}
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Belum ada lowongan</TextCustom>
|
||||
) : (
|
||||
listData.map((item, index) => (
|
||||
<BoxWithHeaderSection
|
||||
key={index}
|
||||
onPress={() => router.push(`/job/${item.id}`)}
|
||||
>
|
||||
<StackCustom>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={item?.Author?.Profile?.imageId}
|
||||
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
|
||||
name={item?.Author?.username}
|
||||
/>
|
||||
|
||||
<TextCustom truncate={2} align="center" bold size="large">
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</BoxWithHeaderSection>
|
||||
))
|
||||
)}
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BaseBox,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
LoaderCustom,
|
||||
ScrollableCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import { jobDataDummy } from "@/screens/Job/listDataDummy";
|
||||
import { useState } from "react";
|
||||
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function JobStatus() {
|
||||
const { user } = useAuth();
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
const [listData, setListData] = useState<any[]>([]);
|
||||
const [isLoadList, setIsLoadList] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [user?.id, activeCategory])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadList(true);
|
||||
const response = await apiJobGetByStatus({
|
||||
authorId: user?.id as string,
|
||||
status: activeCategory as string,
|
||||
});
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
@@ -32,19 +61,24 @@ export default function JobStatus() {
|
||||
|
||||
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>
|
||||
))}
|
||||
{isLoadList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
||||
) : (
|
||||
listData.map((e, i) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/job/${e?.id}/${activeCategory}/detail`}
|
||||
>
|
||||
<TextCustom align="center" bold truncate size="large">
|
||||
{e?.title}
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,51 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
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";
|
||||
import { apiJobGetOne } from "@/service/api-client/api-job";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, 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 [data, setData] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiJobGetOne({ id: id as string });
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
@@ -38,9 +66,23 @@ export default function JobDetailStatus() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail} />
|
||||
<Job_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<StackCustom>
|
||||
<Job_BoxDetailSection data={data} />
|
||||
<Job_ButtonStatusSection
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
isLoading={isLoading}
|
||||
onSetLoading={setIsLoading}
|
||||
isArchive={true}
|
||||
/>
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerCustom
|
||||
|
||||
100
app/(application)/(user)/job/[id]/archive.tsx
Normal file
100
app/(application)/(user)/job/[id]/archive.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import { apiJobGetOne, apiJobUpdateData } from "@/service/api-client/api-job";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function JobDetailArchive() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiJobGetOne({ id: id as string });
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleArchive = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiJobUpdateData({
|
||||
id: id as string,
|
||||
data: false,
|
||||
category: "archive",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<ViewWrapper>
|
||||
<>
|
||||
<StackCustom>
|
||||
<Job_BoxDetailSection data={data} />
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
onPress={() => {
|
||||
handleArchive();
|
||||
}}
|
||||
>
|
||||
Publish kembali
|
||||
</ButtonCustom>
|
||||
{/* <Job_ButtonStatusSection
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
isLoading={isLoading}
|
||||
onSetLoading={setIsLoading}
|
||||
isArchive={true}
|
||||
/> */}
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
</ViewWrapper>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,133 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
DummyLandscapeImage,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { router } from "expo-router";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import { apiJobGetOne, apiJobUpdateData } from "@/service/api-client/api-job";
|
||||
import {
|
||||
deleteFileService,
|
||||
uploadFileService,
|
||||
} from "@/service/upload-service";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function JobEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>({
|
||||
title: "",
|
||||
content: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiJobGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerOnUpdate = async () => {
|
||||
if (!data.title || !data.content || !data.deskripsi) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Harap isi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
let newImageId = "";
|
||||
|
||||
if (imageUri) {
|
||||
const responseUploadImage = await uploadFileService({
|
||||
imageUri: imageUri,
|
||||
dirId: DIRECTORY_ID.job_image,
|
||||
});
|
||||
|
||||
if (responseUploadImage.success) {
|
||||
newImageId = responseUploadImage.data.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (data?.imageId) {
|
||||
const responseDeleteImage = await deleteFileService({
|
||||
id: data.imageId,
|
||||
});
|
||||
|
||||
if (!responseDeleteImage.success) {
|
||||
console.log("[ERROR DELETE IMAGE]", responseDeleteImage.message);
|
||||
}
|
||||
}
|
||||
|
||||
const newData = {
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: newImageId,
|
||||
};
|
||||
|
||||
const response = await apiJobUpdateData({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
category: "edit",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
||||
<ButtonCustom isLoading={isLoading} onPress={() => handlerOnUpdate()}>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
);
|
||||
@@ -23,45 +135,64 @@ export default function JobEdit() {
|
||||
|
||||
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." />
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<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>
|
||||
{imageUri ? (
|
||||
<LandscapeFrameUploaded image={imageUri as any} />
|
||||
) : (
|
||||
<BaseBox>
|
||||
<DummyLandscapeImage imageId={data?.imageId} />
|
||||
</BaseBox>
|
||||
)}
|
||||
|
||||
<Spacing />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
pickImage({
|
||||
setImageUri,
|
||||
});
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
value={data.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Syarat & Kualifikasi"
|
||||
placeholder="Masukan Syarat & Kualifikasi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data.content}
|
||||
onChangeText={(value) => setData({ ...data, content: value })}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Lowongan"
|
||||
placeholder="Masukan Deskripsi Lowongan Kerja"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import {
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { ButtonCustom, LoaderCustom, Spacing, StackCustom, 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 { apiJobGetOne } from "@/service/api-client/api-job";
|
||||
import { BASE_URL } from "@/service/api-config";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Alert, Linking } from "react-native";
|
||||
|
||||
export default function JobDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const jobDetail = jobDataDummy.find((e) => e.id === Number(id));
|
||||
useEffect(() => {
|
||||
onLoadData();
|
||||
}, [id]);
|
||||
|
||||
const OpenLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiJobGetOne({ id: id as string });
|
||||
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const baseUrl = BASE_URL;
|
||||
const linkUrl = `${baseUrl}/job-vacancy/`;
|
||||
|
||||
const OpenLinkButton = ({ id }: { id: string }) => {
|
||||
const jobUrl = `${linkUrl}${id}`;
|
||||
|
||||
const openInBrowser = async () => {
|
||||
const supported = await Linking.canOpenURL(jobUrl);
|
||||
@@ -44,9 +62,8 @@ export default function JobDetail() {
|
||||
);
|
||||
};
|
||||
|
||||
const CopyLinkButton = () => {
|
||||
const jobUrl =
|
||||
"https://stg-hipmi.wibudev.com/job-vacancy/cm6ijt9w8005zucv4twsct657";
|
||||
const CopyLinkButton = ({ id }: { id: string }) => {
|
||||
const jobUrl = `${linkUrl}${id}`;
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await Clipboard.setStringAsync(jobUrl);
|
||||
@@ -70,10 +87,18 @@ export default function JobDetail() {
|
||||
|
||||
return (
|
||||
<ViewWrapper>
|
||||
<Job_BoxDetailSection data={jobDetail}/>
|
||||
<OpenLinkButton />
|
||||
<Spacing />
|
||||
<CopyLinkButton />
|
||||
{isLoading ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<Job_BoxDetailSection data={data} />
|
||||
<StackCustom>
|
||||
<OpenLinkButton id={id as string} />
|
||||
<CopyLinkButton id={id as string} />
|
||||
</StackCustom>
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,19 +7,99 @@ import {
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiJobCreate } from "@/service/api-client/api-job";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function JobCreate() {
|
||||
const nextUrl = "/(application)/(user)/job/(tabs)/status";
|
||||
const { user } = useAuth();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [image, setImage] = useState<string | null>(null);
|
||||
const [data, setData] = useState({
|
||||
title: "",
|
||||
content: "",
|
||||
deskripsi: "",
|
||||
authorId: "",
|
||||
});
|
||||
|
||||
const handlerOnSubmit = async () => {
|
||||
let imageId = "";
|
||||
const newData = {
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
deskripsi: data.deskripsi,
|
||||
authorId: user?.id,
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
if (!data.title || !data.content || !data.deskripsi || !user?.id) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: "Harap isi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
if (image === null || !image) {
|
||||
const response = await apiJobCreate(newData);
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil",
|
||||
text2: "Lowongan berhasil dibuat",
|
||||
});
|
||||
router.replace(nextUrl);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const responseUploadImage = await uploadFileService({
|
||||
imageUri: image,
|
||||
dirId: DIRECTORY_ID.job_image,
|
||||
});
|
||||
|
||||
if (responseUploadImage.success) {
|
||||
imageId = responseUploadImage.data.id;
|
||||
}
|
||||
|
||||
const fixData = {
|
||||
...newData,
|
||||
imageId: imageId,
|
||||
};
|
||||
|
||||
const response = await apiJobCreate(fixData);
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil",
|
||||
text2: "Lowongan berhasil dibuat",
|
||||
});
|
||||
router.replace(nextUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/job/(tabs)/status")
|
||||
}
|
||||
>
|
||||
<ButtonCustom isLoading={isLoading} onPress={() => handlerOnSubmit()}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
<Spacing />
|
||||
@@ -32,10 +112,19 @@ export default function JobCreate() {
|
||||
<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 />
|
||||
{/* <BaseBox>
|
||||
<Image
|
||||
source={image ? { uri: image } : DUMMY_IMAGE.dummy_image}
|
||||
style={{ width: "100%", height: 200 }}
|
||||
/>
|
||||
</BaseBox> */}
|
||||
<LandscapeFrameUploaded image={image as string} />
|
||||
<ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
router.push("/(application)/(image)/take-picture/123");
|
||||
// router.push("/(application)/(image)/take-picture/123");
|
||||
pickImage({
|
||||
setImageUri: setImage,
|
||||
});
|
||||
}}
|
||||
icon="upload"
|
||||
>
|
||||
@@ -48,6 +137,8 @@ export default function JobCreate() {
|
||||
label="Judul Lowongan"
|
||||
placeholder="Masukan Judul Lowongan Kerja"
|
||||
required
|
||||
value={data.title}
|
||||
onChangeText={(value) => setData({ ...data, title: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
@@ -56,6 +147,8 @@ export default function JobCreate() {
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data.content}
|
||||
onChangeText={(value) => setData({ ...data, content: value })}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
@@ -64,6 +157,8 @@ export default function JobCreate() {
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||
/>
|
||||
|
||||
{buttonSubmit()}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { MapCustom, ViewWrapper } from "@/components";
|
||||
import { View } from "react-native";
|
||||
import MapView from "react-native-maps";
|
||||
|
||||
export default function Maps() {
|
||||
return (
|
||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||
<MapCustom height={"100%"} />
|
||||
{/* <MapCustom height={"100%"} /> */}
|
||||
<View style={{ flex: 1 }}>
|
||||
<MapView
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ActionIcon,
|
||||
AvatarComp,
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
Grid,
|
||||
InformationBox,
|
||||
LandscapeFrameUploaded,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
@@ -13,49 +15,122 @@ import {
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
|
||||
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import Portofolio_ButtonCreate from "@/screens/Portofolio/ButtonCreatePortofolio";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
} from "@/service/api-client/api-master";
|
||||
import {
|
||||
IMasterBidangBisnis,
|
||||
IMasterSubBidangBisnis,
|
||||
} from "@/types/Type-Master";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Image } from "expo-image";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import { Avatar } from "react-native-paper";
|
||||
|
||||
export default function PortofolioCreate() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
bidang_usaha: "",
|
||||
sub_bidang_usaha: "",
|
||||
alamat: "",
|
||||
nomor_telepon: "",
|
||||
namaBisnis: "",
|
||||
masterBidangBisnisId: "",
|
||||
alamatKantor: "",
|
||||
tlpn: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
|
||||
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
|
||||
const [subBidangBisnis, setSubBidangBisnis] = useState<
|
||||
IMasterSubBidangBisnis[]
|
||||
>([]);
|
||||
|
||||
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
|
||||
const [listSubBidangSelected, setListSubBidangSelected] = useState([
|
||||
{
|
||||
id: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const [dataMedsos, setDataMedsos] = useState({
|
||||
facebook: "",
|
||||
twitter: "",
|
||||
instagram: "",
|
||||
youtube: "",
|
||||
tiktok: "",
|
||||
});
|
||||
|
||||
const [isLoadingCreate, setIsLoadingCreate] = useState(false);
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
const fixNumber = inputValue.replace(/\s+/g, "");
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log("Selanjutnya");
|
||||
router.replace(`/maps/create`);
|
||||
}
|
||||
useEffect(() => {
|
||||
onLoadMaster();
|
||||
onLoadMasterSubBidangBisnis();
|
||||
}, []);
|
||||
|
||||
const buttonSave = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={handleSave}>Selanjutnya</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
const response = await apiMasterBidangBisnis();
|
||||
setBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadMasterSubBidangBisnis = async () => {
|
||||
try {
|
||||
const response = await apiMasterSubBidangBisnis({});
|
||||
setSubBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setSubBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
|
||||
const selectedList = subBidangBisnis?.filter(
|
||||
(item) => (item?.masterBidangBisnisId as any) === id
|
||||
);
|
||||
setSelectedSubBidang(selectedList as any[]);
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper footerComponent={buttonSave}>
|
||||
<ViewWrapper
|
||||
footerComponent={
|
||||
<Portofolio_ButtonCreate
|
||||
id={id as string}
|
||||
data={data}
|
||||
dataMedsos={dataMedsos}
|
||||
imageUri={imageUri}
|
||||
subBidangSelected={listSubBidangSelected}
|
||||
isLoadingCreate={isLoadingCreate}
|
||||
setIsLoadingCreate={setIsLoadingCreate}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{/* <TextCustom>Portofolio Create {id}</TextCustom> */}
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Lengkapi data bisnis anda." />
|
||||
@@ -63,51 +138,121 @@ export default function PortofolioCreate() {
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
|
||||
onChangeText={(value: any) => setData({ ...data, namaBisnis: value })}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterBidangBisnis.map((item) => ({
|
||||
data={bidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.bidang_usaha}
|
||||
value={data.masterBidangBisnisId}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), bidang_usaha: value });
|
||||
const isSameBidang = data.masterBidangBisnisId === value;
|
||||
|
||||
if (!isSameBidang) {
|
||||
setListSubBidangSelected([{ id: "" }]);
|
||||
}
|
||||
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
handlerSelectedSubBidang({ id: value as string });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<SelectCustom
|
||||
// disabled
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterSubBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
{listSubBidangSelected.map((item, index) => (
|
||||
<SelectCustom
|
||||
key={index}
|
||||
disabled={data.masterBidangBisnisId === ""}
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={_.map(selectedSubBidang as any)
|
||||
.filter((option: any) => {
|
||||
const selectedValues = listSubBidangSelected.map((s) => s.id);
|
||||
return (
|
||||
option.id === item.id || // biarkan tetap muncul kalau ini valuenya sendiri
|
||||
!selectedValues.includes(option.id)
|
||||
);
|
||||
})
|
||||
.map((e: any) => ({
|
||||
value: e.id,
|
||||
label: e.name,
|
||||
}))}
|
||||
value={data.sub_bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), sub_bidang_usaha: value });
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
value={item.id || null}
|
||||
onChange={(value) => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list[index].id = value as any;
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
<CenterCustom>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||||
<ActionIcon
|
||||
disabled={
|
||||
selectedSubBidang.length === listSubBidangSelected.length
|
||||
}
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([
|
||||
...listSubBidangSelected,
|
||||
{ id: "" },
|
||||
]);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={listSubBidangSelected.length <= 1}
|
||||
onPress={() => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list.pop();
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
|
||||
{/* <SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={bidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={null}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
handlerSelectedSubBidang({ id: value as string });
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* <ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([...listSubBidangSelected, { id: "" }]);
|
||||
}}
|
||||
>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
<Spacing /> */}
|
||||
|
||||
{/* <TextCustom>{JSON.stringify(bidangBisnis, null, 2)}</TextCustom> */}
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
@@ -132,6 +277,9 @@ export default function PortofolioCreate() {
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, alamatKantor: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
@@ -144,18 +292,26 @@ export default function PortofolioCreate() {
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
{/* Logo */}
|
||||
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
|
||||
<LandscapeFrameUploaded />
|
||||
|
||||
<CenterCustom>
|
||||
<Avatar.Image
|
||||
source={imageUri ? { uri: imageUri } : DUMMY_IMAGE.dummy_image}
|
||||
size={200}
|
||||
/>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
console.log("Upload logo >>", id);
|
||||
router.navigate(`/(application)/(image)/take-picture/${id}`);
|
||||
pickImage({
|
||||
setImageUri,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
@@ -167,22 +323,37 @@ export default function PortofolioCreate() {
|
||||
<TextInputCustom
|
||||
label="Tiktok"
|
||||
placeholder="Masukkan username tiktok"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, tiktok: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Facebook"
|
||||
placeholder="Masukkan username facebook"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, facebook: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Instagram"
|
||||
placeholder="Masukkan username instagram"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, instagram: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Twitter"
|
||||
placeholder="Masukkan username twitter"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, twitter: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Youtube"
|
||||
placeholder="Masukkan username youtube"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, youtube: value })
|
||||
}
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
|
||||
@@ -1,25 +1,125 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||
import {
|
||||
apiGetOnePortofolio,
|
||||
apiUpdatePortofolio,
|
||||
} from "@/service/api-client/api-portofolio";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { Image } from "expo-image";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function PortofolioEditLogo() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [logoId, setLogoId] = useState<any>();
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { token } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData(id as string);
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
console.log(
|
||||
"Response portofolio >>",
|
||||
JSON.stringify(response.data.logoId, null, 2)
|
||||
);
|
||||
setLogoId(response.data.logoId);
|
||||
};
|
||||
|
||||
async function onUpload() {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await uploadFileService({
|
||||
imageUri,
|
||||
dirId: DIRECTORY_ID.portofolio_logo,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
const fileId = response.data.id;
|
||||
const responseUpdate = await apiUpdatePortofolio({
|
||||
id: id as string,
|
||||
data: { fileId },
|
||||
category: "logo",
|
||||
});
|
||||
|
||||
if (!responseUpdate.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Info",
|
||||
text2: responseUpdate.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (logoId) {
|
||||
const deletePrevFile = await apiFileDelete({
|
||||
token: token as string,
|
||||
id: logoId as string,
|
||||
});
|
||||
|
||||
if (!deletePrevFile.success) {
|
||||
console.log("error delete prev file >>", deletePrevFile.message);
|
||||
}
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Logo berhasil diupdate",
|
||||
});
|
||||
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal",
|
||||
text2: error as string,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
const image = imageUri ? (
|
||||
<Image source={{ uri: imageUri }} style={{ width: 200, height: 200 }} />
|
||||
) : (
|
||||
<Image
|
||||
source={
|
||||
logoId
|
||||
? { uri: API_STRORAGE.GET({ fileId: logoId }) }
|
||||
: DUMMY_IMAGE.avatar
|
||||
}
|
||||
style={{ width: 200, height: 200 }}
|
||||
/>
|
||||
);
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
disabled={isLoading}
|
||||
onPress={() => {
|
||||
console.log("Simpan logo ");
|
||||
router.back();
|
||||
onUpload();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
@@ -34,13 +134,17 @@ export default function PortofolioEditLogo() {
|
||||
height: 250,
|
||||
}}
|
||||
>
|
||||
<AvatarCustom size="xl" />
|
||||
{image}
|
||||
</BaseBox>
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => router.navigate(`/take-picture/${id}`)}
|
||||
onPress={() => {
|
||||
pickImage({
|
||||
setImageUri,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Update
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
|
||||
@@ -4,20 +4,87 @@ import {
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import {
|
||||
apiGetOnePortofolio,
|
||||
apiUpdatePortofolio,
|
||||
} from "@/service/api-client/api-portofolio";
|
||||
import { useLocalSearchParams, router } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function PortofolioEditSocialMedia() {
|
||||
const { id } = useLocalSearchParams();
|
||||
console.log("ID >>", id);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = useState<any>({
|
||||
facebook: "",
|
||||
twitter: "",
|
||||
instagram: "",
|
||||
tiktok: "",
|
||||
youtube: "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData(id as string);
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
console.log(
|
||||
"Response portofolio >>",
|
||||
JSON.stringify(response.data.Portofolio_MediaSosial, null, 2)
|
||||
);
|
||||
const data = response.data.Portofolio_MediaSosial;
|
||||
setData({
|
||||
facebook: data.facebook,
|
||||
twitter: data.twitter,
|
||||
instagram: data.instagram,
|
||||
tiktok: data.tiktok,
|
||||
youtube: data.youtube,
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmitUpdate = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await apiUpdatePortofolio({
|
||||
id: id as string,
|
||||
data: data,
|
||||
category: "medsos",
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Data media terupdate",
|
||||
});
|
||||
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("Error onSubmitUpdate", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonFooter = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
console.log(`Simpan sosmed ${id}`);
|
||||
router.back();
|
||||
}}
|
||||
isLoading={isLoading}
|
||||
disabled={isLoading}
|
||||
onPress={onSubmitUpdate}
|
||||
>
|
||||
Simpan
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
@@ -25,11 +92,36 @@ export default function PortofolioEditSocialMedia() {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonFooter}>
|
||||
<TextInputCustom label="Tiktok" placeholder="Masukkan tiktok" />
|
||||
<TextInputCustom label="Instagram" placeholder="Masukkan instagram" />
|
||||
<TextInputCustom label="Facebook" placeholder="Masukkan facebook" />
|
||||
<TextInputCustom label="Twitter" placeholder="Masukkan twitter" />
|
||||
<TextInputCustom label="Youtube" placeholder="Masukkan youtube" />
|
||||
<TextInputCustom
|
||||
value={data.tiktok}
|
||||
onChangeText={(value) => setData({ ...data, tiktok: value })}
|
||||
label="Tiktok"
|
||||
placeholder="Masukkan tiktok"
|
||||
/>
|
||||
<TextInputCustom
|
||||
value={data.instagram}
|
||||
onChangeText={(value) => setData({ ...data, instagram: value })}
|
||||
label="Instagram"
|
||||
placeholder="Masukkan instagram"
|
||||
/>
|
||||
<TextInputCustom
|
||||
value={data.facebook}
|
||||
onChangeText={(value) => setData({ ...data, facebook: value })}
|
||||
label="Facebook"
|
||||
placeholder="Masukkan facebook"
|
||||
/>
|
||||
<TextInputCustom
|
||||
value={data.twitter}
|
||||
onChangeText={(value) => setData({ ...data, twitter: value })}
|
||||
label="Twitter"
|
||||
placeholder="Masukkan twitter"
|
||||
/>
|
||||
<TextInputCustom
|
||||
value={data.youtube}
|
||||
onChangeText={(value) => setData({ ...data, youtube: value })}
|
||||
label="Youtube"
|
||||
placeholder="Masukkan youtube"
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
357
app/(application)/(user)/portofolio/[id]/edit.back.txt
Normal file
357
app/(application)/(user)/portofolio/[id]/edit.back.txt
Normal file
@@ -0,0 +1,357 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
} from "@/service/api-client/api-master";
|
||||
import {
|
||||
apiGetOnePortofolio,
|
||||
apiUpdatePortofolio,
|
||||
} from "@/service/api-client/api-portofolio";
|
||||
import {
|
||||
IMasterBidangBisnis,
|
||||
IMasterSubBidangBisnis,
|
||||
} from "@/types/Type-Master";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface IFormData {
|
||||
id_Portofolio: string;
|
||||
namaBisnis: string;
|
||||
alamatKantor: string;
|
||||
tlpn: string;
|
||||
deskripsi: string;
|
||||
masterBidangBisnisId: string;
|
||||
subBidang: any[];
|
||||
}
|
||||
|
||||
interface IListSubBidangSelected {
|
||||
id: string;
|
||||
MasterSubBidangBisnis: {
|
||||
id: string;
|
||||
name: string;
|
||||
masterBidangBisnisId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default function PortofolioEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = useState<any>({});
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
|
||||
const [subBidangBisnis, setSubBidangBisnis] = useState<
|
||||
IMasterSubBidangBisnis[]
|
||||
>([]);
|
||||
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
|
||||
const [listSubBidangSelected, setListSubBidangSelected] = useState<
|
||||
IListSubBidangSelected[]
|
||||
>([
|
||||
{
|
||||
id: "",
|
||||
MasterSubBidangBisnis: {
|
||||
id: "",
|
||||
name: "",
|
||||
masterBidangBisnisId: "",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setData({ ...data, tlpn: phoneNumber });
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadMasterBidang();
|
||||
onLoadMasterSubBidangBisnis();
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
|
||||
if (response.data.tlpn && response.data.tlpn.includes("62")) {
|
||||
const fixNumber = response.data.tlpn.replace("62", "");
|
||||
|
||||
setData({ ...response.data, tlpn: fixNumber });
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadMasterBidang = async () => {
|
||||
try {
|
||||
const response = await apiMasterBidangBisnis();
|
||||
setBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
async function onLoadMasterSubBidangBisnis() {
|
||||
try {
|
||||
const response = await apiMasterSubBidangBisnis({});
|
||||
|
||||
if (response.success) {
|
||||
setSubBidangBisnis(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error on load master sub bidang bisnis", error);
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmitUpdate = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
const fixNumber = data.tlpn.replace(/\s+/g, "");
|
||||
const realNumber = callingCode + fixNumber;
|
||||
|
||||
const newData: IFormData = {
|
||||
id_Portofolio: data.id_Portofolio,
|
||||
namaBisnis: data.namaBisnis,
|
||||
alamatKantor: data.alamatKantor,
|
||||
tlpn: realNumber,
|
||||
deskripsi: data.deskripsi,
|
||||
masterBidangBisnisId: data.masterBidangBisnisId,
|
||||
subBidang: listSubBidangSelected,
|
||||
};
|
||||
|
||||
const response = await apiUpdatePortofolio({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
category: "detail",
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Data terupdate",
|
||||
});
|
||||
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("Error handleSubmitUpdate", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonUpdate = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
disabled={isLoading}
|
||||
onPress={handleSubmitUpdate}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonUpdate}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
value={data.namaBisnis}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, namaBisnis: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={bidangBisnis?.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.masterBidangBisnisId}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
}}
|
||||
/>
|
||||
|
||||
{listSubBidangSelected.map((item, index) => (
|
||||
<SelectCustom
|
||||
key={index}
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={subBidangBisnis?.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={item.id || null}
|
||||
onChange={(value) => {
|
||||
console.log("Value >>", value);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<CenterCustom>
|
||||
<View
|
||||
style={{ flexDirection: "row", alignItems: "center", gap: 10 }}
|
||||
>
|
||||
<ActionIcon
|
||||
// disabled={
|
||||
// selectedSubBidang.length === listSubBidangSelected.length
|
||||
// }
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([
|
||||
...listSubBidangSelected,
|
||||
{
|
||||
id: "",
|
||||
MasterSubBidangBisnis: {
|
||||
id: "",
|
||||
name: "",
|
||||
masterBidangBisnisId: "",
|
||||
},
|
||||
},
|
||||
]);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
// disabled={listSubBidangSelected.length <= 1}
|
||||
onPress={() => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list.pop();
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
|
||||
{/* <Grid>
|
||||
<Grid.Col span={10}>
|
||||
<SelectCustom
|
||||
// disabled
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterSubBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.masterSubBidangBisnisId}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), masterSubBidangBisnisId: value });
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<TouchableOpacity onPress={() => console.log("delete")}>
|
||||
<Ionicons name="trash" size={24} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</Grid.Col>
|
||||
</Grid> */}
|
||||
{/* <ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing /> */}
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
Nomor Telepon
|
||||
</TextCustom>
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInput
|
||||
value={data.tlpn}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
value={data.alamatKantor}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, alamatKantor: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Bisnis"
|
||||
placeholder="Masukkan deskripsi bisnis"
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, deskripsi: value })
|
||||
}
|
||||
autosize
|
||||
minRows={2}
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
<TextCustom>{JSON.stringify(subBidangBisnis, null, 2)}</TextCustom>
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
CenterCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
@@ -12,46 +13,319 @@ import {
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
|
||||
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
} from "@/service/api-client/api-master";
|
||||
import {
|
||||
apiGetOnePortofolio,
|
||||
apiUpdatePortofolio,
|
||||
} from "@/service/api-client/api-portofolio";
|
||||
import {
|
||||
IMasterBidangBisnis,
|
||||
IMasterSubBidangBisnis,
|
||||
} from "@/types/Type-Master";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import { ActivityIndicator } from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface IFormData {
|
||||
id_Portofolio: string;
|
||||
namaBisnis: string;
|
||||
alamatKantor: string;
|
||||
tlpn: string;
|
||||
deskripsi: string;
|
||||
masterBidangBisnisId: string;
|
||||
subBidang: any[];
|
||||
}
|
||||
|
||||
interface IListSubBidangSelected {
|
||||
id: string;
|
||||
MasterSubBidangBisnis?: {
|
||||
id?: string;
|
||||
name?: string;
|
||||
masterBidangBisnisId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default function PortofolioEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = useState<any>({});
|
||||
|
||||
const [data, setData] = useState({
|
||||
name: "",
|
||||
bidang_usaha: "",
|
||||
sub_bidang_usaha: "",
|
||||
alamat: "",
|
||||
nomor_telepon: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [bidangBisnis, setBidangBisnis] = useState<
|
||||
IMasterBidangBisnis[] | null
|
||||
>(null);
|
||||
const [subBidangBisnis, setSubBidangBisnis] = useState<
|
||||
IMasterSubBidangBisnis[] | null
|
||||
>(null);
|
||||
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
|
||||
const [listSubBidangSelected, setListSubBidangSelected] = useState<
|
||||
IListSubBidangSelected[]
|
||||
>([]);
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
setData({ ...data, tlpn: phoneNumber });
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log(`Update portofolio berhasil ${id}`);
|
||||
router.back();
|
||||
const onLoadMasterBidang = async () => {
|
||||
try {
|
||||
const response = await apiMasterBidangBisnis();
|
||||
setBidangBisnis(response.data);
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
setBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
async function onLoadMasterSubBidangBisnis() {
|
||||
try {
|
||||
const response = await apiMasterSubBidangBisnis({});
|
||||
setSubBidangBisnis(response.data);
|
||||
|
||||
return response.success;
|
||||
} catch (error) {
|
||||
console.error("Error on load master sub bidang bisnis", error);
|
||||
}
|
||||
}
|
||||
|
||||
const handleLoadMaster = async (id: string) => {
|
||||
const loadBidang = await onLoadMasterBidang();
|
||||
const loadSubBidang = await onLoadMasterSubBidangBisnis();
|
||||
|
||||
if (!loadBidang || !loadSubBidang) {
|
||||
return;
|
||||
}
|
||||
|
||||
onLoadData(id);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleLoadMaster(id as any);
|
||||
}, [id]);
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
|
||||
if (response.success) {
|
||||
const fixNumber = response.data.tlpn.replace("62", "");
|
||||
setData({ ...response.data, tlpn: fixNumber });
|
||||
|
||||
// Cek apakah ada sub bidang bisnis yang terpilih
|
||||
const prevSubBidang = response.data.Portofolio_BidangDanSubBidangBisnis;
|
||||
if (prevSubBidang && prevSubBidang.length > 0) {
|
||||
setListSubBidangSelected(prevSubBidang);
|
||||
} else {
|
||||
// Jika tidak ada sub bidang yang terpilih sebelumnya, tetap inisialisasi dengan array kosong
|
||||
setListSubBidangSelected([
|
||||
{
|
||||
id: "",
|
||||
MasterSubBidangBisnis: {
|
||||
id: "",
|
||||
name: "",
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
const bisnisId = response.data.masterBidangBisnisId;
|
||||
handleLoadSelectedSubBidang({
|
||||
id: bisnisId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Handler untuk saat komponen pertama kali load
|
||||
const handleLoadSelectedSubBidang = ({ id }: { id: string }) => {
|
||||
if (!subBidangBisnis) return;
|
||||
|
||||
const filteredSubBidang: any = subBidangBisnis.filter((item) => {
|
||||
return item.masterBidangBisnisId === id;
|
||||
});
|
||||
setSelectedSubBidang(filteredSubBidang);
|
||||
};
|
||||
|
||||
// Handler untuk menambah sub bidang bisnis
|
||||
const handleAddSubBidang = () => {
|
||||
setListSubBidangSelected([
|
||||
...listSubBidangSelected,
|
||||
{
|
||||
id: "",
|
||||
MasterSubBidangBisnis: { id: "", name: "" },
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
// Handler untuk menghapus sub bidang bisnis
|
||||
const handleRemoveSubBidang = (index: number) => {
|
||||
if (listSubBidangSelected.length <= 1) return;
|
||||
|
||||
const updatedList = [...listSubBidangSelected];
|
||||
updatedList.splice(index, 1);
|
||||
setListSubBidangSelected(updatedList);
|
||||
};
|
||||
|
||||
// Handler untuk perubahan bidang bisnis
|
||||
const handleBidangBisnisChange = (val: string) => {
|
||||
const isSameBidang = data?.MasterBidangBisnis?.id === val;
|
||||
|
||||
setData({ ...(data as any), masterBidangBisnisId: val });
|
||||
|
||||
// Reset sub bidang jika ganti bidang
|
||||
if (!isSameBidang) {
|
||||
setListSubBidangSelected([
|
||||
{
|
||||
id: "",
|
||||
MasterSubBidangBisnis: { id: "", name: "" },
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
handleLoadSelectedSubBidang({ id: val });
|
||||
};
|
||||
|
||||
// Handler untuk update sub bidang
|
||||
const handleSubBidangChange = (value: string, index: number) => {
|
||||
const select = selectedSubBidang.find((sub: any) => sub.id === value);
|
||||
const list: any = _.cloneDeep(listSubBidangSelected);
|
||||
list[index] = {
|
||||
id: "",
|
||||
MasterSubBidangBisnis: select || {
|
||||
id: value,
|
||||
name: "",
|
||||
masterBidangBisnisId: "",
|
||||
},
|
||||
};
|
||||
setListSubBidangSelected(list);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (subBidangBisnis?.length !== undefined && data.masterBidangBisnisId) {
|
||||
handleLoadSelectedSubBidang({
|
||||
id: data.masterBidangBisnisId,
|
||||
});
|
||||
}
|
||||
}, [subBidangBisnis, data.masterBidangBisnisId]);
|
||||
|
||||
function validateData(data: any) {
|
||||
if (
|
||||
!data.namaBisnis ||
|
||||
!data.alamatKantor ||
|
||||
!data.tlpn ||
|
||||
!data.deskripsi ||
|
||||
!data.masterBidangBisnisId
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateDataSubBidang(dataArray: any[]) {
|
||||
return !dataArray.some(
|
||||
(item: any) =>
|
||||
!item.MasterSubBidangBisnis.id ||
|
||||
item.MasterSubBidangBisnis.id.trim() === ""
|
||||
);
|
||||
}
|
||||
|
||||
const handleSubmitUpdate = async () => {
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
const fixNumber = data.tlpn.replace(/\s+/g, "");
|
||||
const realNumber = callingCode + fixNumber;
|
||||
|
||||
const newData: IFormData = {
|
||||
id_Portofolio: data.id_Portofolio,
|
||||
namaBisnis: data.namaBisnis,
|
||||
alamatKantor: data.alamatKantor,
|
||||
tlpn: realNumber,
|
||||
deskripsi: data.deskripsi,
|
||||
masterBidangBisnisId: data.masterBidangBisnisId,
|
||||
subBidang: listSubBidangSelected,
|
||||
};
|
||||
|
||||
if (!validateData(newData)) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Harap lengkapi data",
|
||||
});
|
||||
}
|
||||
|
||||
if (!validateDataSubBidang(listSubBidangSelected as any)) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Harap lengkapi sub bidang",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await apiUpdatePortofolio({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
category: "detail",
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Info",
|
||||
text2: response.message,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Data terupdate",
|
||||
});
|
||||
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("Error handleSubmitUpdate", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonUpdate = (
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom onPress={handleSave}>Simpan</ButtonCustom>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
disabled={isLoading}
|
||||
onPress={handleSubmitUpdate}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
if (!bidangBisnis || !subBidangBisnis) {
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<ActivityIndicator size="large" color={MainColor.yellow} />
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper footerComponent={buttonUpdate}>
|
||||
@@ -60,50 +334,98 @@ export default function PortofolioEdit() {
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
value={data.namaBisnis}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, namaBisnis: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterBidangBisnis.map((item) => ({
|
||||
data={bidangBisnis?.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), bidang_usaha: value });
|
||||
value={data.masterBidangBisnisId}
|
||||
onChange={(value: any) => {
|
||||
handleBidangBisnisChange(value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
{listSubBidangSelected.map((item, index) => {
|
||||
// Filter data untuk select sub bidang, menghilangkan yang sudah dipilih kecuali untuk item ini sendiri
|
||||
const selectedIds = listSubBidangSelected
|
||||
.filter((_, i) => i !== index)
|
||||
.map((s) => s.MasterSubBidangBisnis?.id)
|
||||
.filter((id) => id); // Filter hanya yang memiliki id (tidak kosong)
|
||||
|
||||
const availableSubBidangOptions = (selectedSubBidang || [])
|
||||
.filter((sub: any) => {
|
||||
// Tampilkan jika ini adalah opsi yang dipilih saat ini atau belum dipilih di sub bidang lainnya
|
||||
|
||||
return (
|
||||
sub.id === item.MasterSubBidangBisnis?.id ||
|
||||
!selectedIds.includes(sub.id)
|
||||
);
|
||||
})
|
||||
.map((sub: any) => ({
|
||||
value: sub.id,
|
||||
label: sub.name,
|
||||
}));
|
||||
|
||||
return (
|
||||
<SelectCustom
|
||||
// disabled
|
||||
key={index}
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={dummyMasterSubBidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.sub_bidang_usaha}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), sub_bidang_usaha: value });
|
||||
data={availableSubBidangOptions}
|
||||
value={item.MasterSubBidangBisnis?.id || null}
|
||||
onChange={(value: any) => {
|
||||
handleSubBidangChange(value, index);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
);
|
||||
})}
|
||||
|
||||
<CenterCustom>
|
||||
<View
|
||||
style={{ flexDirection: "row", alignItems: "center", gap: 10 }}
|
||||
>
|
||||
<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>
|
||||
<ActionIcon
|
||||
disabled={
|
||||
selectedSubBidang.length === listSubBidangSelected.length
|
||||
}
|
||||
onPress={() => {
|
||||
handleAddSubBidang();
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={listSubBidangSelected.length <= 1}
|
||||
onPress={() => {
|
||||
handleRemoveSubBidang(listSubBidangSelected.length - 1);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
@@ -113,7 +435,7 @@ export default function PortofolioEdit() {
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
value={data.tlpn}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
@@ -127,6 +449,10 @@ export default function PortofolioEdit() {
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
value={data.alamatKantor}
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, alamatKantor: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
@@ -141,7 +467,7 @@ export default function PortofolioEdit() {
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={100}
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
import { AlertCustom, DrawerCustom } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { DrawerCustom, LoaderCustom, Spacing, StackCustom } from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Portofolio_BusinessLocation from "@/screens/Portofolio/BusinessLocationSection";
|
||||
import Portofolio_ButtonDelete from "@/screens/Portofolio/ButtonDelete";
|
||||
import Portofolio_Data from "@/screens/Portofolio/DataPortofolio";
|
||||
import { drawerItemsPortofolio } from "@/screens/Portofolio/ListPage";
|
||||
import Portofolio_MenuDrawerSection from "@/screens/Portofolio/MenuDrawer";
|
||||
import PorfofolioSection from "@/screens/Portofolio/PorfofolioSection";
|
||||
import Portofolio_SocialMediaSection from "@/screens/Portofolio/SocialMediaSection";
|
||||
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Stack, useLocalSearchParams, router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function Portofolio() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
const [profileId, setProfileId] = useState<any>();
|
||||
|
||||
const { user } = useAuth();
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -22,15 +33,41 @@ export default function Portofolio() {
|
||||
const closeDrawer = () => {
|
||||
setIsDrawerOpen(false);
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadUserByToken();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
async function onLoadData(id: string) {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
console.log(
|
||||
"[PROFILE ID]>>",
|
||||
JSON.stringify(response.data.Profile.id, null, 2)
|
||||
);
|
||||
setData(response.data);
|
||||
}
|
||||
|
||||
const onLoadUserByToken = async () => {
|
||||
const response = await apiUser(user?.id as string);
|
||||
console.log(
|
||||
"[PROFILE LOGIN]>>",
|
||||
JSON.stringify(response.data?.Profile.id, null, 2)
|
||||
);
|
||||
setProfileId(response?.data?.Profile?.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
{/* Header */}
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
{/* Header */}
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () =>
|
||||
data?.Profile?.id !== profileId ? null : (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
@@ -39,40 +76,44 @@ export default function Portofolio() {
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
<PorfofolioSection setShowDeleteAlert={setDeleteAlert} />
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
{!data || !profileId ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
<Portofolio_Data
|
||||
data={data}
|
||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||
/>
|
||||
<Portofolio_BusinessLocation />
|
||||
<Portofolio_SocialMediaSection
|
||||
data={data?.Portofolio_MediaSosial}
|
||||
/>
|
||||
<Portofolio_ButtonDelete
|
||||
id={id as string}
|
||||
isLoadingDelete={isLoadingDelete}
|
||||
setIsLoadingDelete={setIsLoadingDelete}
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
<DrawerCustom
|
||||
isVisible={isDrawerOpen}
|
||||
closeDrawer={closeDrawer}
|
||||
height={350}
|
||||
height={"auto"}
|
||||
>
|
||||
<Portofolio_MenuDrawerSection
|
||||
drawerItems={drawerItemsPortofolio({ id: id as string })}
|
||||
setIsDrawerOpen={setIsDrawerOpen}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
{/* Alert Delete */}
|
||||
<AlertCustom
|
||||
isVisible={deleteAlert}
|
||||
onLeftPress={() => setDeleteAlert(false)}
|
||||
onRightPress={() => {
|
||||
setDeleteAlert(false);
|
||||
console.log("Hapus portofolio");
|
||||
router.back();
|
||||
}}
|
||||
title="Hapus Portofolio"
|
||||
message="Apakah Anda yakin ingin menghapus portofolio ini?"
|
||||
textLeft="Batal"
|
||||
textRight="Hapus"
|
||||
colorRight={MainColor.red}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
import { BaseBox, Grid, TextCustom, ViewWrapper } from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { TextCustom, ViewWrapper } from "@/components";
|
||||
import Portofolio_BoxView from "@/screens/Portofolio/BoxPortofolioView";
|
||||
import { apiGetPortofolio } from "@/service/api-client/api-portofolio";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function ListPortofolio() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadPortofolio(id as string);
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadPortofolio = async (id: string) => {
|
||||
const response = await apiGetPortofolio({ id: id });
|
||||
setData(response.data);
|
||||
};
|
||||
return (
|
||||
<ViewWrapper>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<BaseBox
|
||||
key={index}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
onPress={() => {
|
||||
console.log("press to Portofolio");
|
||||
router.push(`/portofolio/${id}`);
|
||||
}}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col
|
||||
span={10}
|
||||
style={{ justifyContent: "center", backgroundColor: "" }}
|
||||
>
|
||||
<TextCustom bold size="large" truncate={1}>
|
||||
Nama usaha portofolio
|
||||
</TextCustom>
|
||||
<TextCustom size="small" color="yellow">
|
||||
#id-porofolio12345
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={2}
|
||||
style={{ alignItems: "flex-end", justifyContent: "center" }}
|
||||
>
|
||||
<Ionicons
|
||||
name="caret-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
{data ? data?.map((item: any, index: number) => (
|
||||
<Portofolio_BoxView key={index} data={item} />
|
||||
)) : <TextCustom>Tidak ada portofolio</TextCustom>}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { LoaderCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||
@@ -5,16 +7,15 @@ import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
||||
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
|
||||
import Profile_PortofolioSection from "@/screens/Profile/PortofolioSection";
|
||||
import ProfileSection from "@/screens/Profile/ProfileSection";
|
||||
import { apiGetPortofolio } from "@/service/api-client/api-portofolio";
|
||||
import { apiProfile } from "@/service/api-client/api-profile";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { IProfile } from "@/types/Type-Profile";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams
|
||||
} from "expo-router";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
@@ -22,8 +23,10 @@ export default function Profile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [data, setData] = useState<IProfile>();
|
||||
const [dataToken, setDataToken] = useState<IProfile>();
|
||||
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
||||
|
||||
const { logout, isAdmin } = useAuth();
|
||||
const { logout, isAdmin, user } = useAuth();
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -36,36 +39,72 @@ export default function Profile() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadPortofolio(id as string);
|
||||
onLoadUserByToken();
|
||||
isUserCheck();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
async function onLoadData(id: string) {
|
||||
const isUserCheck = () => {
|
||||
const userId = id;
|
||||
const userLoginId = dataToken?.id;
|
||||
|
||||
return userId === userLoginId;
|
||||
};
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiProfile({ id: id });
|
||||
setData(response.data);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadUserByToken = async () => {
|
||||
const response = await apiUser(user?.id as string);
|
||||
setDataToken(response?.data?.Profile);
|
||||
};
|
||||
|
||||
const onLoadPortofolio = async (id: string) => {
|
||||
const response = await apiGetPortofolio({ id: id });
|
||||
const lastTwoByDate = response.data
|
||||
.sort(
|
||||
(a: any, b: any) =>
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
) // urut desc
|
||||
.slice(0, 2);
|
||||
setListPortofolio(lastTwoByDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "Profile",
|
||||
title: `Profile`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<ButtonnDot
|
||||
id={id as string}
|
||||
openDrawer={openDrawer}
|
||||
isUserCheck={isUserCheck()}
|
||||
logout={logout}
|
||||
/>
|
||||
),
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
{/* Main View */}
|
||||
<ViewWrapper>
|
||||
{/* Header */}
|
||||
<ProfileSection data={data as any} />
|
||||
{!data || !dataToken ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<ProfileSection data={data as any} />
|
||||
|
||||
<Profile_PortofolioSection
|
||||
data={listPortofolio as any}
|
||||
profileId={id as string}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
@@ -83,3 +122,44 @@ export default function Profile() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const ButtonnDot = ({
|
||||
id,
|
||||
openDrawer,
|
||||
isUserCheck,
|
||||
logout,
|
||||
}: {
|
||||
id: string;
|
||||
openDrawer: () => void;
|
||||
isUserCheck: boolean;
|
||||
logout: () => Promise<void>;
|
||||
}) => {
|
||||
const isId = id === undefined || id === null;
|
||||
|
||||
console.log("ID CHECK", id);
|
||||
|
||||
if (isId) {
|
||||
console.log("ID UNDEFINED", id);
|
||||
return (
|
||||
<>
|
||||
<TouchableOpacity onPress={logout}>
|
||||
<Ionicons name="log-out" size={20} color={MainColor.red} />
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isUserCheck && (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
} from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
||||
import { uploadImageService } from "@/service/upload-service";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import { IProfile } from "@/types/Type-Profile";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
@@ -22,6 +24,7 @@ export default function UpdateBackgroundProfile() {
|
||||
const [data, setData] = useState<IProfile>();
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { token } = useAuth();
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -32,10 +35,6 @@ export default function UpdateBackgroundProfile() {
|
||||
async function onLoadData(id: string) {
|
||||
try {
|
||||
const response = await apiProfile({ id });
|
||||
console.log(
|
||||
"response image id >>",
|
||||
JSON.stringify(response.data.backgroundId, null, 2)
|
||||
);
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("error get profile >>", error);
|
||||
@@ -46,7 +45,7 @@ export default function UpdateBackgroundProfile() {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await uploadImageService({
|
||||
const response = await uploadFileService({
|
||||
imageUri,
|
||||
dirId: DIRECTORY_ID.profile_background,
|
||||
});
|
||||
@@ -68,12 +67,23 @@ export default function UpdateBackgroundProfile() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data?.imageBackgroundId) {
|
||||
const deletePrevFile = await apiFileDelete({
|
||||
token: token as string,
|
||||
id: data?.imageBackgroundId as string,
|
||||
});
|
||||
|
||||
if (!deletePrevFile.success) {
|
||||
console.log("error delete prev file >>", deletePrevFile.message);
|
||||
}
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Background berhasil diupdate",
|
||||
});
|
||||
|
||||
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,8 +8,10 @@ import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
||||
import { uploadImageService } from "@/service/upload-service";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import { IProfile } from "@/types/Type-Profile";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
@@ -22,6 +24,7 @@ export default function UpdatePhotoProfile() {
|
||||
const [data, setData] = useState<IProfile>();
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { token } = useAuth();
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -32,6 +35,7 @@ export default function UpdatePhotoProfile() {
|
||||
async function onLoadData(id: string) {
|
||||
try {
|
||||
const response = await apiProfile({ id });
|
||||
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("error get profile >>", error);
|
||||
@@ -42,13 +46,11 @@ export default function UpdatePhotoProfile() {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await uploadImageService({
|
||||
const response = await uploadFileService({
|
||||
imageUri,
|
||||
dirId: DIRECTORY_ID.profile_foto,
|
||||
});
|
||||
|
||||
console.log("response upload photo>>", JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
const fileId = response.data.id;
|
||||
const responseUpdate = await apiUpdateProfile({
|
||||
@@ -66,12 +68,23 @@ export default function UpdatePhotoProfile() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data?.imageId) {
|
||||
const deletePrevFile = await apiFileDelete({
|
||||
token: token as string,
|
||||
id: data?.imageId as string,
|
||||
});
|
||||
|
||||
if (!deletePrevFile.success) {
|
||||
console.log("error delete prev file >>", deletePrevFile.message);
|
||||
}
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Sukses",
|
||||
text2: "Photo berhasil diupdate",
|
||||
});
|
||||
|
||||
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
|
||||
import InformationBox from "@/components/Box/InformationBox";
|
||||
@@ -15,7 +15,7 @@ import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiCreateProfile } from "@/service/api-client/api-profile";
|
||||
import { apiValidationEmail } from "@/service/api-client/api-validation";
|
||||
import { uploadImageService } from "@/service/upload-service";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
@@ -69,7 +69,7 @@ export default function CreateProfile() {
|
||||
|
||||
if (imagePhoto) {
|
||||
try {
|
||||
const responseUploadPhoto = await uploadImageService({
|
||||
const responseUploadPhoto = await uploadFileService({
|
||||
imageUri: imagePhoto,
|
||||
dirId: DIRECTORY_ID.profile_foto,
|
||||
});
|
||||
@@ -90,7 +90,7 @@ export default function CreateProfile() {
|
||||
|
||||
if (imageBackground) {
|
||||
try {
|
||||
const responseUploadBackground = await uploadImageService({
|
||||
const responseUploadBackground = await uploadFileService({
|
||||
imageUri: imageBackground,
|
||||
dirId: DIRECTORY_ID.profile_background,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
AvatarComp,
|
||||
ClickableCustom,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
@@ -10,39 +11,46 @@ import {
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { apiAllUser } from "@/service/api-client/api-user";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function UserSearch() {
|
||||
function generateRandomPhoneNumber(index: number) {
|
||||
let prefix;
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
const [isLoadList, setIsLoadList] = useState(false);
|
||||
|
||||
// Menentukan prefix berdasarkan index genap atau ganjil
|
||||
if (index % 2 === 0) {
|
||||
const evenPrefixes = ["6288", "6289", "6281"];
|
||||
prefix = evenPrefixes[Math.floor(Math.random() * evenPrefixes.length)];
|
||||
} else {
|
||||
const oddPrefixes = ["6285", "6283"];
|
||||
prefix = oddPrefixes[Math.floor(Math.random() * oddPrefixes.length)];
|
||||
useEffect(() => {
|
||||
onLoadData(search);
|
||||
}, [search]);
|
||||
|
||||
const onLoadData = async (search: string) => {
|
||||
try {
|
||||
setIsLoadList(true);
|
||||
const response = await apiAllUser({ search: search });
|
||||
console.log("[DATA USER] >", JSON.stringify(response.data, null, 2));
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("Error fetching data", error);
|
||||
} finally {
|
||||
setIsLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Menghitung panjang sisa nomor acak (antara 10 - 12 digit)
|
||||
const remainingLength = Math.floor(Math.random() * 3) + 10; // 10, 11, atau 12
|
||||
const handleSearch = (search: string) => {
|
||||
setSearch(search);
|
||||
onLoadData(search);
|
||||
};
|
||||
|
||||
// Membuat sisa nomor acak
|
||||
let randomNumber = "";
|
||||
for (let i = 0; i < remainingLength; i++) {
|
||||
randomNumber += Math.floor(Math.random() * 10); // Digit acak antara 0-9
|
||||
}
|
||||
|
||||
// Menggabungkan prefix dan sisa nomor
|
||||
return prefix + randomNumber;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
value={search}
|
||||
onChangeText={handleSearch}
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search"
|
||||
@@ -57,41 +65,48 @@ export default function UserSearch() {
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
{Array.from({ length: 20 }).map((e, index) => {
|
||||
return (
|
||||
<Grid key={index}>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom href={`/profile/${index}`}/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<TextCustom size="large">Nama user {index}</TextCustom>
|
||||
<TextCustom size="small">
|
||||
+{generateRandomPhoneNumber(index)}
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
{isLoadList ? (
|
||||
<LoaderCustom />
|
||||
) : !_.isEmpty(data) ? (
|
||||
data?.map((e, index) => {
|
||||
return (
|
||||
<ClickableCustom
|
||||
key={index}
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${e?.Profile?.id}`);
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${index}`);
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarComp fileId={e?.Profile?.imageId} size="base" />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom size="large">{e?.username}</TextCustom>
|
||||
<TextCustom size="small">+{e?.nomor}</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</ClickableCustom>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<TextCustom align="center">Tidak ditemukan</TextCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
|
||||
@@ -1,15 +1,57 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
LoaderCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState, useCallback } from "react";
|
||||
|
||||
export default function VotingContribution() {
|
||||
const { user } = useAuth();
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetAll({
|
||||
category: "contribution",
|
||||
authorId: user?.id as string,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper hideFooter>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada kontribusi</TextCustom>
|
||||
) : listData.map((item: any, index: number) => (
|
||||
<Voting_BoxPublishSection
|
||||
data={item}
|
||||
key={index}
|
||||
href={`/voting/${index}/contribution`}
|
||||
href={`/voting/${item.id}/contribution`}
|
||||
/>
|
||||
))}
|
||||
</ViewWrapper>
|
||||
|
||||
@@ -1,11 +1,44 @@
|
||||
import { ViewWrapper } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
||||
import TabsTwoButtonCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { useState } from "react";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useCallback, useState } from "react";
|
||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
|
||||
export default function VotingHistory() {
|
||||
const { user } = useAuth();
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
||||
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [activeCategory])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetAll({
|
||||
category: activeCategory === "all" ? "all-history" : "my-history",
|
||||
authorId: user?.id as string,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
@@ -25,13 +58,20 @@ export default function VotingHistory() {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 10 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection
|
||||
key={index}
|
||||
id={activeCategory as any}
|
||||
href={`/voting/${index}/history`}
|
||||
/>
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada riwayat</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<Voting_BoxPublishSection
|
||||
key={index}
|
||||
id={item.id}
|
||||
data={item}
|
||||
href={`/voting/${item.id}/history`}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,68 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
FloatingButton,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
ViewWrapper
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
||||
import { router } from "expo-router";
|
||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function VotingBeranda() {
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetAll({
|
||||
search,
|
||||
category: "beranda",
|
||||
});
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ViewWrapper
|
||||
hideFooter
|
||||
floatingButton={
|
||||
<FloatingButton onPress={() => router.push("/voting/create")} />
|
||||
}
|
||||
headerComponent={<SearchInput placeholder="Cari voting" />}
|
||||
headerComponent={
|
||||
<SearchInput placeholder="Cari voting" onChangeText={setSearch} />
|
||||
}
|
||||
>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Voting_BoxPublishSection key={index} href={`/voting/${index}`} />
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<Voting_BoxPublishSection
|
||||
data={item}
|
||||
key={index}
|
||||
href={`/voting/${item.id}`}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,52 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
LoaderCustom,
|
||||
ScrollableCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
import { apiVotingGetByStatus } from "@/service/api-client/api-voting";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function VotingStatus() {
|
||||
const { user } = useAuth();
|
||||
const id = user?.id || "";
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||
"publish"
|
||||
);
|
||||
|
||||
const [listData, setListData] = useState([]);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [activeCategory, id])
|
||||
);
|
||||
|
||||
async function onLoadData() {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetByStatus({
|
||||
id: id as string,
|
||||
status: activeCategory!,
|
||||
});
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
@@ -34,27 +66,33 @@ export default function VotingStatus() {
|
||||
|
||||
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>
|
||||
))}
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, i: number) => (
|
||||
<BaseBox
|
||||
key={i}
|
||||
paddingTop={20}
|
||||
paddingBottom={20}
|
||||
href={`/voting/${item.id}/${activeCategory}/detail`}
|
||||
>
|
||||
<StackCustom>
|
||||
<TextCustom align="center" bold truncate={2} size="large">
|
||||
{item?.title || ""}
|
||||
</TextCustom>
|
||||
<BadgeCustom
|
||||
style={{ width: "70%", alignSelf: "center" }}
|
||||
variant="light"
|
||||
>
|
||||
{item?.awalVote && dateTimeView({date: item?.awalVote, withoutTime: true})} -{" "}
|
||||
{item?.akhirVote && dateTimeView({date: item?.akhirVote, withoutTime: true})}
|
||||
</BadgeCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,63 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BackButton,
|
||||
BaseBox,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
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";
|
||||
import {
|
||||
apiVotingGetOne,
|
||||
apiVotingUpdateData,
|
||||
} from "@/service/api-client/api-voting";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function VotingDetailStatus() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
|
||||
const [data, setData] = useState<any>(null);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||
console.log("PATH >> ", item.path);
|
||||
@@ -32,9 +72,24 @@ export default function VotingDetailStatus() {
|
||||
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
onPressRight: async () => {
|
||||
try {
|
||||
const response = await apiVotingUpdateData({
|
||||
id: id as string,
|
||||
data: data.isArsip ? false : true,
|
||||
category: "archive",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -46,7 +101,7 @@ export default function VotingDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: `Detail ${status}`,
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
@@ -57,9 +112,37 @@ export default function VotingDetailStatus() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailSection />
|
||||
<Voting_ButtonStatusSection status={status as string} />
|
||||
<Spacing />
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
{status === "publish" && (
|
||||
<BaseBox>
|
||||
<TextCustom bold>
|
||||
Status:{" "}
|
||||
<TextCustom color={data?.isArsip ? "red" : "green"}>
|
||||
{data?.isArsip ? "Arsip" : "Publish"}
|
||||
</TextCustom>
|
||||
</TextCustom>
|
||||
</BaseBox>
|
||||
)}
|
||||
<Spacing height={0} />
|
||||
<Voting_BoxDetailSection data={data as any} />
|
||||
{status === "publish" ? (
|
||||
<Voting_BoxDetailHasilVotingSection
|
||||
listData={data?.Voting_DaftarNamaVote}
|
||||
/>
|
||||
) : (
|
||||
<Voting_ButtonStatusSection
|
||||
isLoading={isLoading}
|
||||
onSetLoading={setIsLoading}
|
||||
id={id as string}
|
||||
status={status as string}
|
||||
/>
|
||||
)}
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Draft Drawer ========= */}
|
||||
|
||||
@@ -1,22 +1,76 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import {
|
||||
apiVotingContribution,
|
||||
apiVotingGetOne,
|
||||
} from "@/service/api-client/api-voting";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function VotingDetailContribution() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [nameChoice, setNameChoice] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
handlerLoadData();
|
||||
}, [id, user?.id]);
|
||||
|
||||
async function handlerLoadData() {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
await onLoadData();
|
||||
await onLoadCheckContribution();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadCheckContribution = async () => {
|
||||
try {
|
||||
const response = await apiVotingContribution({
|
||||
id: id as string,
|
||||
authorId: user?.id as string,
|
||||
category: "checked",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setNameChoice(response.data.nameChoice);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
router.navigate(item.path as any);
|
||||
@@ -36,11 +90,27 @@ export default function VotingDetailContribution() {
|
||||
/>
|
||||
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailContributionSection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
<Spacing />
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<Voting_BoxDetailContributionSection
|
||||
data={data}
|
||||
nameChoice={nameChoice}
|
||||
headerAvatar={
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={data?.Author?.Profile?.imageId || ""}
|
||||
name={data?.Author?.username || "Username"}
|
||||
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection
|
||||
listData={data?.Voting_DaftarNamaVote}
|
||||
/>
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
|
||||
@@ -1,29 +1,184 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
CenterCustom,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import {
|
||||
apiVotingGetOne,
|
||||
apiVotingUpdateData,
|
||||
} from "@/service/api-client/api-voting";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
interface IEditData {
|
||||
title?: string;
|
||||
deskripsi?: string;
|
||||
awalVote?: string;
|
||||
akhirVote?: string;
|
||||
Voting_DaftarNamaVote?: [
|
||||
{
|
||||
value?: string;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
export default function VotingEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [data, setData] = useState<IEditData>();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
|
||||
if (response.success) {
|
||||
const data = response.data;
|
||||
setData({
|
||||
title: data.title,
|
||||
deskripsi: data.deskripsi,
|
||||
awalVote: data.awalVote,
|
||||
akhirVote: data.akhirVote,
|
||||
Voting_DaftarNamaVote: data.Voting_DaftarNamaVote,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const validateDateRange = ({
|
||||
selectedStratDate,
|
||||
selectedEndDate,
|
||||
}: {
|
||||
selectedStratDate: string | Date;
|
||||
selectedEndDate: string | Date;
|
||||
}): { isValid: boolean; error?: string } => {
|
||||
const startDate = new Date(selectedStratDate);
|
||||
const endDate = new Date(selectedEndDate);
|
||||
|
||||
// Cek apakah tanggal valid
|
||||
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: "Invalid date provided",
|
||||
};
|
||||
}
|
||||
|
||||
if (startDate >= endDate) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: "Ubah tanggal berakhirnya event",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
error: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const validateForm = async () => {
|
||||
if (!data?.title || !data?.deskripsi) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Lengkapi semua data",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data?.Voting_DaftarNamaVote?.some((item: any) => item.value === "")) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Isi semua data pilihan",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const startDate = new Date(data?.awalVote as any);
|
||||
const endDate = new Date(data?.akhirVote as any);
|
||||
|
||||
if (startDate >= endDate) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Ubah tanggal berakhirnya event",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handlerUpdateSubmit = async () => {
|
||||
const isValid = await validateForm();
|
||||
if (!isValid) return;
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const newData = {
|
||||
...data,
|
||||
awalVote: new Date(data?.awalVote as any).toISOString(),
|
||||
akhirVote: new Date(data?.akhirVote as any).toISOString(),
|
||||
listVote: data?.Voting_DaftarNamaVote?.map((item: any) => item.value),
|
||||
};
|
||||
|
||||
const response = await apiVotingUpdateData({
|
||||
id: id as string,
|
||||
data: newData,
|
||||
category: "edit",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
return router.back();
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: response.message,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.back()
|
||||
}
|
||||
isLoading={isLoading}
|
||||
onPress={() => handlerUpdateSubmit()}
|
||||
>
|
||||
Update
|
||||
</ButtonCustom>
|
||||
@@ -34,45 +189,144 @@ export default function VotingEdit() {
|
||||
|
||||
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 />
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextInputCustom
|
||||
label="Judul Voting"
|
||||
placeholder="MasukanJudul Voting"
|
||||
required
|
||||
value={data?.title}
|
||||
onChangeText={(text) => setData({ ...data, title: text })}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
placeholder="Masukan Deskripsi"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data?.deskripsi}
|
||||
onChangeText={(text) => setData({ ...data, deskripsi: text })}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<Spacing />
|
||||
|
||||
<DateTimePickerCustom
|
||||
minimumDate={new Date(Date.now())}
|
||||
label="Mulai Voting"
|
||||
required
|
||||
value={new Date(data?.awalVote as any)}
|
||||
onChange={(date: any) => {
|
||||
setData({ ...data, awalVote: date });
|
||||
}}
|
||||
/>
|
||||
|
||||
<StackCustom gap={0}>
|
||||
<DateTimePickerCustom
|
||||
minimumDate={new Date(data?.awalVote as any)}
|
||||
label="Voting Berakhir"
|
||||
required
|
||||
value={new Date(data?.akhirVote as any)}
|
||||
onChange={(date: any) => {
|
||||
setData({ ...data, akhirVote: date });
|
||||
}}
|
||||
/>
|
||||
|
||||
{validateDateRange({
|
||||
selectedStratDate: data?.awalVote as any,
|
||||
selectedEndDate: data?.akhirVote as any,
|
||||
}).isValid ? (
|
||||
<TextCustom style={{ color: "green" }}>
|
||||
{
|
||||
validateDateRange({
|
||||
selectedStratDate: data?.awalVote as any,
|
||||
selectedEndDate: data?.akhirVote as any,
|
||||
}).error
|
||||
}
|
||||
</TextCustom>
|
||||
) : (
|
||||
<TextCustom style={{ color: "red" }}>
|
||||
{
|
||||
validateDateRange({
|
||||
selectedStratDate: data?.awalVote as any,
|
||||
selectedEndDate: data?.akhirVote as any,
|
||||
}).error
|
||||
}
|
||||
</TextCustom>
|
||||
)}
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
|
||||
{data?.Voting_DaftarNamaVote?.map((item: any, index: number) => (
|
||||
<TextInputCustom
|
||||
key={index}
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
value={item.value}
|
||||
onChangeText={(value: any) =>
|
||||
setData({
|
||||
...(data as any),
|
||||
Voting_DaftarNamaVote: data?.Voting_DaftarNamaVote?.map(
|
||||
(item: any, i: any) =>
|
||||
i === index ? { ...item, value } : item
|
||||
),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</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>
|
||||
<CenterCustom>
|
||||
<View
|
||||
style={{ flexDirection: "row", alignItems: "center", gap: 10 }}
|
||||
>
|
||||
<ActionIcon
|
||||
disabled={(data as any)?.Voting_DaftarNamaVote?.length >= 4}
|
||||
onPress={() => {
|
||||
setData({
|
||||
...(data as any),
|
||||
Voting_DaftarNamaVote: [
|
||||
...(data as any)?.Voting_DaftarNamaVote,
|
||||
{ value: "" },
|
||||
],
|
||||
});
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={
|
||||
((data as any)?.Voting_DaftarNamaVote?.length as any) <= 2
|
||||
}
|
||||
onPress={() => {
|
||||
const list = _.clone((data as any)?.Voting_DaftarNamaVote);
|
||||
list.pop();
|
||||
setData({
|
||||
...(data as any),
|
||||
Voting_DaftarNamaVote: list,
|
||||
});
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,78 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BackButton,
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import { Voting_BoxDetailHistorySection } from "@/screens/Voting/BoxDetailHistorySection";
|
||||
import {
|
||||
apiVotingContribution,
|
||||
apiVotingGetOne,
|
||||
} from "@/service/api-client/api-voting";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function VotingDetailHistory() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [nameChoice, setNameChoice] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
handlerLoadData();
|
||||
}, [id, user?.id]);
|
||||
|
||||
async function handlerLoadData() {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
await onLoadData();
|
||||
await onLoadCheckContribution();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadCheckContribution = async () => {
|
||||
try {
|
||||
const response = await apiVotingContribution({
|
||||
id: id as string,
|
||||
authorId: user?.id as string,
|
||||
category: "checked",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setNameChoice(response.data.nameChoice);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
router.navigate(item.path as any);
|
||||
setOpenDrawerPublish(false);
|
||||
@@ -35,11 +90,27 @@ export default function VotingDetailHistory() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<Voting_BoxDetailHistorySection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
<Spacing />
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<Voting_BoxDetailHistorySection
|
||||
data={data}
|
||||
nameChoice={nameChoice}
|
||||
headerAvatar={
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={data?.Author?.Profile?.imageId || ""}
|
||||
name={data?.Author?.username || "Username"}
|
||||
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Voting_BoxDetailHasilVotingSection
|
||||
listData={data?.Voting_DaftarNamaVote}
|
||||
/>
|
||||
<Spacing />
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
AvatarUsernameAndOtherComponent,
|
||||
@@ -5,20 +6,87 @@ import {
|
||||
DotButton,
|
||||
DrawerCustom,
|
||||
InformationBox,
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconArchive, IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
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";
|
||||
import {
|
||||
apiVotingContribution,
|
||||
apiVotingGetOne,
|
||||
apiVotingUpdateData,
|
||||
} from "@/service/api-client/api-voting";
|
||||
import { today } from "@/utils/dateTimeView";
|
||||
import {
|
||||
router,
|
||||
Stack,
|
||||
useFocusEffect,
|
||||
useLocalSearchParams,
|
||||
} from "expo-router";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function VotingDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||
const [isContribution, setIsContribution] = useState(false);
|
||||
const [nameChoice, setNameChoice] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadData();
|
||||
}, [id, user?.id])
|
||||
);
|
||||
|
||||
async function handlerLoadData() {
|
||||
try {
|
||||
setLoadingGetData(true);
|
||||
await onLoadData();
|
||||
await onLoadCheckContribution();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadingGetData(false);
|
||||
}
|
||||
}
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadCheckContribution = async () => {
|
||||
try {
|
||||
const response = await apiVotingContribution({
|
||||
id: id as string,
|
||||
authorId: user?.id as string,
|
||||
category: "checked",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setIsContribution(response.data.isContribution);
|
||||
setNameChoice(response.data.nameChoice);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePressPublish = (item: IMenuDrawerItem) => {
|
||||
if (item.path === "") {
|
||||
AlertDefaultSystem({
|
||||
@@ -26,9 +94,24 @@ export default function VotingDetail() {
|
||||
message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("Hapus");
|
||||
router.back();
|
||||
onPressRight: async () => {
|
||||
try {
|
||||
const response = await apiVotingUpdateData({
|
||||
id: id as string,
|
||||
data: data.isArsip ? false : true,
|
||||
category: "archive",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: response.message,
|
||||
});
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -49,15 +132,32 @@ export default function VotingDetail() {
|
||||
/>
|
||||
|
||||
<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." />
|
||||
{loadingGetData ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom gap={"xs"}>
|
||||
{today.getDate() < new Date(data?.awalVote).getDate() && (
|
||||
<InformationBox text="Untuk sementara voting tidak dapat dilakukan. Voting dapat dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." />
|
||||
)}
|
||||
<Voting_BoxDetailPublishSection
|
||||
data={data}
|
||||
userId={user?.id as string}
|
||||
isContribution={isContribution}
|
||||
nameChoice={nameChoice}
|
||||
headerAvatar={
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={data?.Author?.Profile?.imageId || ""}
|
||||
name={data?.Author?.username || "Username"}
|
||||
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Voting_BoxDetailPublishSection
|
||||
headerAvatar={<AvatarUsernameAndOtherComponent />}
|
||||
/>
|
||||
|
||||
<Voting_BoxDetailHasilVotingSection />
|
||||
</StackCustom>
|
||||
<Voting_BoxDetailHasilVotingSection
|
||||
listData={data?.Voting_DaftarNamaVote}
|
||||
/>
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* ========= Publish Drawer ========= */}
|
||||
@@ -67,18 +167,28 @@ export default function VotingDetail() {
|
||||
height={"auto"}
|
||||
>
|
||||
<MenuDrawerDynamicGrid
|
||||
data={[
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
{
|
||||
icon: <IconArchive />,
|
||||
label: "Update Arsip",
|
||||
path: "" as any,
|
||||
},
|
||||
]}
|
||||
data={
|
||||
user?.id === data?.Author?.id
|
||||
? [
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
{
|
||||
icon: <IconArchive />,
|
||||
label: "Update Arsip",
|
||||
path: "" as any,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
icon: <IconContribution />,
|
||||
label: "Daftar Kontributor",
|
||||
path: `/voting/${id}/list-of-contributor`,
|
||||
},
|
||||
]
|
||||
}
|
||||
onPressItem={handlePressPublish as any}
|
||||
/>
|
||||
</DrawerCustom>
|
||||
|
||||
@@ -1,26 +1,69 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AvatarUsernameAndOtherComponent,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
LoaderCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { apiVotingContribution } from "@/service/api-client/api-voting";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export default function Voting_ListOfContributor() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [listData, setListData] = useState<any>([]);
|
||||
const [isLoadData, setIsLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadList();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadList = async () => {
|
||||
try {
|
||||
setIsLoadData(true);
|
||||
const response = await apiVotingContribution({
|
||||
id: id as string,
|
||||
authorId: "",
|
||||
category: "list",
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
))}
|
||||
{isLoadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada kontributor</TextCustom>
|
||||
) : (
|
||||
listData.map((item: any, index: number) => (
|
||||
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
|
||||
<AvatarUsernameAndOtherComponent
|
||||
avatar={item?.Author?.Profile?.imageId || ""}
|
||||
name={item?.Author?.username || "Username"}
|
||||
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
|
||||
rightComponent={
|
||||
<BadgeCustom style={{ alignSelf: "flex-end" }}>
|
||||
{item?.Voting_DaftarNamaVote?.value}
|
||||
</BadgeCustom>
|
||||
}
|
||||
/>
|
||||
</BaseBox>
|
||||
))
|
||||
)}
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,30 +1,103 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCenteredOnly,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
CenterCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiVotingCreate } from "@/service/api-client/api-voting";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import _ from "lodash";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function VotingCreate() {
|
||||
const { user } = useAuth();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = useState({
|
||||
authorId: "",
|
||||
title: "",
|
||||
deskripsi: "",
|
||||
awalVote: "",
|
||||
akhirVote: "",
|
||||
listVote: [],
|
||||
});
|
||||
|
||||
const [listVote, setListVote] = useState([
|
||||
{
|
||||
name: "Nama Pilihan",
|
||||
value: "",
|
||||
},
|
||||
{
|
||||
name: "Nama Pilihan",
|
||||
value: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const handlerSubmit = async () => {
|
||||
if (!data.title || !data.deskripsi || !data.awalVote || !data.akhirVote) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Lengkapi semua data",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (listVote.some((item: any) => item.value === "")) {
|
||||
Toast.show({
|
||||
type: "info",
|
||||
text1: "Lengkapi semua data pilihan",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const newData = {
|
||||
...data,
|
||||
authorId: user?.id,
|
||||
awalVote: new Date(data.awalVote as any).toISOString(),
|
||||
akhirVote: new Date(data.akhirVote as any).toISOString(),
|
||||
listVote: listVote,
|
||||
};
|
||||
|
||||
const response = await apiVotingCreate(newData);
|
||||
// console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Data berhasil disimpan",
|
||||
});
|
||||
router.replace("/(application)/(user)/voting/(tabs)/status");
|
||||
} else {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Data gagal disimpan",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = () => {
|
||||
return (
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
onPress={() =>
|
||||
router.replace("/(application)/(user)/voting/(tabs)/status")
|
||||
}
|
||||
>
|
||||
<ButtonCustom isLoading={isLoading} onPress={() => handlerSubmit()}>
|
||||
Simpan
|
||||
</ButtonCustom>
|
||||
</BoxButtonOnFooter>
|
||||
@@ -39,6 +112,8 @@ export default function VotingCreate() {
|
||||
label="Judul Voting"
|
||||
placeholder="MasukanJudul Voting"
|
||||
required
|
||||
value={data.title}
|
||||
onChangeText={(value: any) => setData({ ...data, title: value })}
|
||||
/>
|
||||
<TextAreaCustom
|
||||
label="Deskripsi"
|
||||
@@ -46,31 +121,81 @@ export default function VotingCreate() {
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
|
||||
/>
|
||||
<DateTimePickerCustom
|
||||
label="Mulai Voting"
|
||||
required
|
||||
// value={data.awalVote ? new Date(data.awalVote) : null}
|
||||
onChange={(value: any) => setData({ ...data, awalVote: value })}
|
||||
minimumDate={new Date(Date.now())}
|
||||
/>
|
||||
<DateTimePickerCustom
|
||||
disabled={!data.awalVote}
|
||||
label="Voting Berakhir"
|
||||
required
|
||||
// value={data.akhirVote ? new Date(data.akhirVote) : null}
|
||||
onChange={(value: any) => setData({ ...data, akhirVote: value })}
|
||||
minimumDate={
|
||||
data.awalVote ? new Date(data.awalVote) : new Date(Date.now())
|
||||
}
|
||||
/>
|
||||
<DateTimePickerCustom label="Mulai Voting" required />
|
||||
<DateTimePickerCustom label="Voting Berakhir" required />
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={10}>
|
||||
<TextInputCustom
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
|
||||
{listVote.map((item, index) => (
|
||||
<TextInputCustom
|
||||
key={index}
|
||||
label="Pilihan"
|
||||
placeholder="Masukan Pilihan"
|
||||
required
|
||||
value={item.value}
|
||||
onChangeText={(value: any) =>
|
||||
setListVote(
|
||||
listVote.map((item, i) =>
|
||||
i === index ? { ...item, value } : item
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
|
||||
<CenterCustom>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||||
<ActionIcon
|
||||
disabled={listVote.length >= 4}
|
||||
onPress={() => {
|
||||
setListVote([...listVote, { name: "Nama Pilihan", value: "" }]);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</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>
|
||||
<ActionIcon
|
||||
disabled={listVote.length <= 2}
|
||||
onPress={() => {
|
||||
const list = _.clone(listVote);
|
||||
list.pop();
|
||||
setListVote(list);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
|
||||
<ButtonCenteredOnly onPress={() => console.log("add")}>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
AvatarCustom,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import DIRECTORY_ID from "@/constants/directory-id";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { uploadImageService } from "@/service/upload-service";
|
||||
import { uploadFileService } from "@/service/upload-service";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
@@ -21,7 +21,7 @@ export default function ScreenUpload() {
|
||||
async function onUpload() {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await uploadImageService({
|
||||
const response = await uploadFileService({
|
||||
imageUri,
|
||||
dirId: DIRECTORY_ID.profile_foto,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { apiConfig } from "@/service/api-config";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export async function tryUploadService({
|
||||
export default async function tryUploadService({
|
||||
dirId,
|
||||
imageUri,
|
||||
}: {
|
||||
|
||||
BIN
assets/images/dummy/dummy-image.jpg
Normal file
BIN
assets/images/dummy/dummy-image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -9,11 +9,13 @@ export default function ActionIcon({
|
||||
onPress,
|
||||
icon,
|
||||
size = "md",
|
||||
disabled = false,
|
||||
}: {
|
||||
href?: Href;
|
||||
onPress?: () => void;
|
||||
icon: React.ReactNode;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
const sizeMap = {
|
||||
xs: 22,
|
||||
@@ -25,7 +27,7 @@ export default function ActionIcon({
|
||||
|
||||
const getSize = (size: SizeType): DimensionValue => {
|
||||
if (!size) return sizeMap.md; // Default to 'md' if size is undefined
|
||||
if (typeof size === 'string' && size in sizeMap) {
|
||||
if (typeof size === "string" && size in sizeMap) {
|
||||
return sizeMap[size as keyof typeof sizeMap];
|
||||
}
|
||||
return size as DimensionValue;
|
||||
@@ -35,9 +37,10 @@ export default function ActionIcon({
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
disabled={disabled}
|
||||
activeOpacity={0.7}
|
||||
style={{
|
||||
backgroundColor: MainColor.yellow,
|
||||
backgroundColor: disabled ? MainColor.disabled : MainColor.yellow,
|
||||
padding: 5,
|
||||
borderRadius: 50,
|
||||
alignItems: "center",
|
||||
@@ -46,6 +49,7 @@ export default function ActionIcon({
|
||||
height: iconSize,
|
||||
}}
|
||||
onPress={() => {
|
||||
if (disabled) return;
|
||||
if (href) {
|
||||
router.push(href);
|
||||
} else {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function BaseBox({
|
||||
|
||||
return (
|
||||
<>
|
||||
{onPress || href ? (
|
||||
{onPress as any || href ? (
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.7}
|
||||
onPress={href ? () => router.navigate(href) : onPress}
|
||||
|
||||
@@ -10,15 +10,18 @@ interface ButtonCenteredOnlyProps {
|
||||
icon?: "plus" | "upload" | string;
|
||||
onPress: () => void;
|
||||
isLoading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
export default function ButtonCenteredOnly({
|
||||
onPress,
|
||||
children,
|
||||
icon = "plus",
|
||||
isLoading = false,
|
||||
disabled = false,
|
||||
}: ButtonCenteredOnlyProps) {
|
||||
return (
|
||||
<ButtonCustom
|
||||
disabled={disabled}
|
||||
isLoading={isLoading}
|
||||
onPress={onPress}
|
||||
iconLeft={
|
||||
|
||||
@@ -61,7 +61,7 @@ const ButtonCustom: React.FC<ButtonProps> = ({
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
{/* Render icon jika tersedia */}
|
||||
{iconLeft && iconLeft}
|
||||
{isLoading ? "" : iconLeft && iconLeft}
|
||||
{isLoading ? (
|
||||
<ActivityIndicator size={18} color={MainColor.darkblue} />
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
// DateTimeInput.tsx
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
@@ -119,7 +119,7 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
|
||||
<Ionicons
|
||||
name="calendar-outline"
|
||||
size={20}
|
||||
color={MainColor.placeholder}
|
||||
color={disabled ? MainColor.white_gray : MainColor.placeholder}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -131,8 +131,8 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
|
||||
}}
|
||||
>
|
||||
<Grid.Col span={6} style={{}}>
|
||||
<Pressable onPress={toggleDatePicker}>
|
||||
<TextCustom color="gray">
|
||||
<Pressable onPress={() => !disabled && toggleDatePicker()}>
|
||||
<TextCustom color={disabled ? "default" : "gray"}>
|
||||
{selectedDate ? (
|
||||
<TextCustom color="black">
|
||||
{selectedDate.toLocaleDateString()}
|
||||
@@ -148,8 +148,8 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
|
||||
</Grid.Col>
|
||||
|
||||
<Grid.Col span={5} style={{}}>
|
||||
<Pressable onPress={toggleTimePicker}>
|
||||
<TextCustom color="gray">
|
||||
<Pressable onPress={() => !disabled && toggleTimePicker()}>
|
||||
<TextCustom color={disabled ? "default" : "gray"}>
|
||||
{selectedTime ? (
|
||||
<TextCustom color="black">
|
||||
{selectedTime.toLocaleTimeString("id-ID", {
|
||||
|
||||
@@ -3,24 +3,19 @@ import { MainColor } from "@/constants/color-palet";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import DateTimePicker, {
|
||||
DateTimePickerEvent,
|
||||
DateTimePickerEvent,
|
||||
} from "@react-native-community/datetimepicker";
|
||||
import dayjs from "dayjs";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
StyleProp,
|
||||
Text,
|
||||
View,
|
||||
ViewStyle
|
||||
} from "react-native";
|
||||
import { StyleProp, Text, View, ViewStyle } from "react-native";
|
||||
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
|
||||
interface DateTimeInputProps {
|
||||
// Main
|
||||
value?: DateTimePickerEvent;
|
||||
value?: DateTimePickerEvent | Date | null;
|
||||
mode?: "date" | "time";
|
||||
onChange: (selectedDate: DateTimePickerEvent) => void;
|
||||
onChange: (selectedDate: DateTimePickerEvent | Date | null) => void;
|
||||
maximumDate?: Date;
|
||||
minimumDate?: Date;
|
||||
// Main
|
||||
@@ -74,7 +69,7 @@ const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
|
||||
<ClickableCustom
|
||||
activeOpacity={0.8}
|
||||
style={[GStyles.inputContainerArea, containerStyle]}
|
||||
onPress={handlePress}
|
||||
onPress={() => !disabled && handlePress()}
|
||||
>
|
||||
{label && (
|
||||
<Text style={GStyles.inputLabel}>
|
||||
@@ -95,11 +90,11 @@ const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
|
||||
<Ionicons
|
||||
name="calendar-outline"
|
||||
size={20}
|
||||
color={MainColor.placeholder}
|
||||
color={disabled ? MainColor.white : MainColor.placeholder}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<TextCustom color="gray">
|
||||
<TextCustom color={disabled ? "default" : "gray"}>
|
||||
{selectedDate ? (
|
||||
<TextCustom color="black">
|
||||
{dayjs(selectedDate).format("DD-MM-YYYY HH:mm")}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
|
||||
import {
|
||||
DateTimePickerEvent,
|
||||
} from "@react-native-community/datetimepicker";
|
||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||
import React from "react";
|
||||
import { Platform } from "react-native";
|
||||
import DateTimeInput_Android from "./DataTimeAndroid";
|
||||
import DateTimeInput_IOS from "./DateTimeIOS";
|
||||
|
||||
type Props = {
|
||||
value?: Date;
|
||||
value?: Date | DateTimePickerEvent | null;
|
||||
onChange?: (date: Date) => void;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
maximumDate?: Date;
|
||||
minimumDate?: Date;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const DateTimePickerCustom: React.FC<Props> = ({
|
||||
@@ -23,18 +21,21 @@ const DateTimePickerCustom: React.FC<Props> = ({
|
||||
required,
|
||||
maximumDate,
|
||||
minimumDate,
|
||||
disabled = false,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{Platform.OS === "ios" ? (
|
||||
<DateTimeInput_IOS
|
||||
label={label}
|
||||
onChange={(date: DateTimePickerEvent) => {
|
||||
onChange={(date: DateTimePickerEvent | Date | null) => {
|
||||
onChange?.(date as any);
|
||||
}}
|
||||
required={required}
|
||||
maximumDate={maximumDate}
|
||||
minimumDate={minimumDate}
|
||||
disabled={disabled}
|
||||
value={value as DateTimePickerEvent | Date | null}
|
||||
/>
|
||||
) : (
|
||||
<DateTimeInput_Android
|
||||
@@ -45,6 +46,7 @@ const DateTimePickerCustom: React.FC<Props> = ({
|
||||
required={required}
|
||||
maximumDate={maximumDate}
|
||||
minimumDate={minimumDate}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||
@@ -5,19 +6,23 @@ import { IMenuDrawerItem } from "../_Interface/types";
|
||||
import { Href } from "expo-router";
|
||||
|
||||
type IMenuDrawerItemProps = {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
value?: string;
|
||||
path?: Href | string;
|
||||
color?: string;
|
||||
}
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
value?: string;
|
||||
path?: Href | string;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
interface MenuDrawerDynamicGridProps {
|
||||
data: IMenuDrawerItemProps[];
|
||||
columns?: number;
|
||||
onPressItem?: (item: IMenuDrawerItemProps) => void;
|
||||
data: IMenuDrawerItemProps[];
|
||||
columns?: number;
|
||||
onPressItem?: (item: IMenuDrawerItemProps) => void;
|
||||
}
|
||||
const MenuDrawerDynamicGrid = ({ data, columns = 4, onPressItem }: MenuDrawerDynamicGridProps) => {
|
||||
const MenuDrawerDynamicGrid = ({
|
||||
data,
|
||||
columns = 4,
|
||||
onPressItem,
|
||||
}: MenuDrawerDynamicGridProps) => {
|
||||
const numColumns = columns;
|
||||
|
||||
return (
|
||||
|
||||
@@ -16,6 +16,7 @@ const sizeMap = {
|
||||
|
||||
interface AvatarCompProps {
|
||||
fileId?: string;
|
||||
fileIdDefault?: string;
|
||||
size: Size;
|
||||
onPress?: () => void | any;
|
||||
href?: Href | undefined | any;
|
||||
@@ -23,6 +24,7 @@ interface AvatarCompProps {
|
||||
|
||||
export default function AvatarComp({
|
||||
fileId,
|
||||
fileIdDefault,
|
||||
size,
|
||||
onPress,
|
||||
href = `/(application)/(image)/preview-image/${fileId}`,
|
||||
@@ -34,7 +36,11 @@ export default function AvatarComp({
|
||||
<Avatar.Image
|
||||
size={dimension}
|
||||
source={
|
||||
fileId ? { uri: API_STRORAGE.GET({ fileId }) } : DUMMY_IMAGE.avatar
|
||||
fileId
|
||||
? { uri: API_STRORAGE.GET({ fileId }) }
|
||||
: fileIdDefault
|
||||
? fileIdDefault
|
||||
: DUMMY_IMAGE.avatar
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,11 @@ import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { Image } from "react-native";
|
||||
import BaseBox from "../Box/BaseBox";
|
||||
|
||||
export default function LandscapeFrameUploaded() {
|
||||
export default function LandscapeFrameUploaded({
|
||||
image,
|
||||
}: {
|
||||
image?: string;
|
||||
}) {
|
||||
return (
|
||||
<BaseBox
|
||||
style={{
|
||||
@@ -11,7 +15,7 @@ export default function LandscapeFrameUploaded() {
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
source={DUMMY_IMAGE.background}
|
||||
source={image ? { uri: image } : DUMMY_IMAGE.dummy_image}
|
||||
resizeMode="cover"
|
||||
style={{ width: "100%", height: "100%", borderRadius: 10 }}
|
||||
/>
|
||||
|
||||
8
components/Loader/LoaderCustom.tsx
Normal file
8
components/Loader/LoaderCustom.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ActivityIndicator } from "react-native";
|
||||
|
||||
export default function LoaderCustom({ size }: { size?: "small" | "large" }) {
|
||||
return (
|
||||
<ActivityIndicator size={size ? size : "small"} color={MainColor.yellow} />
|
||||
);
|
||||
}
|
||||
92
components/Modal/ModalCustom.tsx
Normal file
92
components/Modal/ModalCustom.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { TEXT_SIZE_LARGE } from "@/constants/constans-value";
|
||||
import React from "react";
|
||||
import {
|
||||
Keyboard,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
interface AlertCustomProps {
|
||||
children: React.ReactNode;
|
||||
isVisible: boolean;
|
||||
}
|
||||
|
||||
export default function ModalCustom({
|
||||
children,
|
||||
isVisible,
|
||||
}: AlertCustomProps) {
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.alertBox}>
|
||||
<View style={{ width: "100%" }}>{children}</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: "rgba(0,0,0,0.5)",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
zIndex: 999,
|
||||
paddingVertical: 20,
|
||||
},
|
||||
alertBox: {
|
||||
width: "90%",
|
||||
backgroundColor: MainColor.darkblue,
|
||||
borderColor: AccentColor.blue,
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
padding: 20,
|
||||
alignItems: "center",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.25,
|
||||
elevation: 5,
|
||||
},
|
||||
alertTitle: {
|
||||
fontSize: TEXT_SIZE_LARGE,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 20,
|
||||
color: MainColor.white_gray,
|
||||
},
|
||||
alertMessage: {
|
||||
textAlign: "center",
|
||||
marginBottom: 20,
|
||||
color: MainColor.white_gray,
|
||||
},
|
||||
alertButtons: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
},
|
||||
alertButton: {
|
||||
flex: 1,
|
||||
padding: 10,
|
||||
borderRadius: 50,
|
||||
marginHorizontal: 5,
|
||||
alignItems: "center",
|
||||
},
|
||||
leftButton: {
|
||||
backgroundColor: "gray",
|
||||
},
|
||||
rightButton: {
|
||||
backgroundColor: MainColor.green,
|
||||
},
|
||||
buttonText: {
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ImageSourcePropType } from "react-native";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import Divider from "../Divider/Divider";
|
||||
import Grid from "../Grid/GridCustom";
|
||||
import AvatarCustom from "../Image/AvatarCustom";
|
||||
import AvatarComp from "../Image/AvatarComp";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
|
||||
const AvatarUsernameAndOtherComponent = ({
|
||||
@@ -12,7 +12,7 @@ const AvatarUsernameAndOtherComponent = ({
|
||||
withBottomLine = false,
|
||||
}: {
|
||||
avatarHref?: string;
|
||||
avatar?: ImageSourcePropType;
|
||||
avatar?: string;
|
||||
name?: string;
|
||||
rightComponent?: React.ReactNode;
|
||||
withBottomLine?: boolean;
|
||||
@@ -21,7 +21,7 @@ const AvatarUsernameAndOtherComponent = ({
|
||||
<>
|
||||
<Grid containerStyle={{ zIndex: 10 }}>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom source={avatar} href={avatarHref as any} />
|
||||
<AvatarComp fileId={avatar} href={avatarHref as any} size="base" />
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={rightComponent ? 6 : 10}
|
||||
@@ -40,7 +40,7 @@ const AvatarUsernameAndOtherComponent = ({
|
||||
</Grid.Col>
|
||||
)}
|
||||
</Grid>
|
||||
{withBottomLine && <Divider marginTop={0} />}
|
||||
{withBottomLine && <Divider color={AccentColor.blue} marginTop={0} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,17 +4,33 @@ import { Image } from "expo-image";
|
||||
import { StyleSheet } from "react-native";
|
||||
import ClickableCustom from "../Clickable/ClickableCustom";
|
||||
import { router } from "expo-router";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
|
||||
export default function DummyLandscapeImage({height, unClickPath}: {height?: number, unClickPath?: boolean}) {
|
||||
export default function DummyLandscapeImage({
|
||||
height,
|
||||
unClickPath,
|
||||
imageId,
|
||||
}: {
|
||||
height?: number;
|
||||
unClickPath?: boolean;
|
||||
imageId?: string;
|
||||
}) {
|
||||
return (
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
if (!unClickPath) {
|
||||
router.push("/(application)/(image)/preview-image/1");
|
||||
router.push(`/(application)/(image)/preview-image/${imageId}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Image source={DUMMY_IMAGE.background} style={[styles.backgroundImage, {height: height || 200}]} />
|
||||
<Image
|
||||
source={
|
||||
imageId
|
||||
? { uri: API_STRORAGE.GET({ fileId: imageId }) }
|
||||
: DUMMY_IMAGE.dummy_image
|
||||
}
|
||||
style={[styles.backgroundImage, { height: height || 200 }]}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
);
|
||||
}
|
||||
|
||||
63
components/_ShareComponent/PdfViewer.tsx
Normal file
63
components/_ShareComponent/PdfViewer.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
// PdfViewer.tsx
|
||||
import React, { useState } from "react";
|
||||
import { ActivityIndicator, StyleSheet, View } from "react-native";
|
||||
import WebView from "react-native-webview";
|
||||
|
||||
interface PdfViewerProps {
|
||||
uri: string; // URL PDF dari API
|
||||
}
|
||||
|
||||
const PdfViewer: React.FC<PdfViewerProps> = ({ uri }) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color="#007AFF" />
|
||||
</View>
|
||||
)}
|
||||
<WebView
|
||||
source={{ uri }}
|
||||
style={styles.webView}
|
||||
onLoadEnd={() => setLoading(false)}
|
||||
onError={(syntheticEvent) => {
|
||||
const { nativeEvent } = syntheticEvent;
|
||||
console.warn("WebView error:", nativeEvent);
|
||||
setLoading(false);
|
||||
}}
|
||||
scalesPageToFit={true}
|
||||
javaScriptEnabled={true}
|
||||
domStorageEnabled={true}
|
||||
originWhitelist={["*"]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// const { width, height } = Dimensions.get("window");
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: "#f0f0f0",
|
||||
},
|
||||
loadingContainer: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "rgba(255,255,255,0.8)",
|
||||
zIndex: 1,
|
||||
},
|
||||
webView: {
|
||||
// width: width,
|
||||
// height: height,
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default PdfViewer;
|
||||
@@ -11,6 +11,7 @@ interface SearchInputProps {
|
||||
iconRight?: React.ReactNode;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
style?: StyleProp<TextStyle>;
|
||||
onChangeText?: (value: string) => void;
|
||||
}
|
||||
export default function SearchInput({
|
||||
placeholder,
|
||||
@@ -19,6 +20,7 @@ export default function SearchInput({
|
||||
iconRight,
|
||||
containerStyle,
|
||||
style,
|
||||
onChangeText,
|
||||
...props
|
||||
}: SearchInputProps) {
|
||||
return (
|
||||
@@ -30,6 +32,7 @@ export default function SearchInput({
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
onChangeText={onChangeText}
|
||||
placeholder={placeholder}
|
||||
borderRadius={50}
|
||||
containerStyle={[containerStyle, { marginBottom: 0 }]}
|
||||
|
||||
@@ -61,6 +61,8 @@ import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
|
||||
import GridComponentView from "./_ShareComponent/GridSectionView";
|
||||
// Progress
|
||||
import ProgressCustom from "./Progress/ProgressCustom";
|
||||
// Loader
|
||||
import LoaderCustom from "./Loader/LoaderCustom";
|
||||
|
||||
export {
|
||||
// ActionIcon
|
||||
@@ -128,4 +130,6 @@ export {
|
||||
TextInputCustom,
|
||||
// ViewWrapper
|
||||
ViewWrapper,
|
||||
// Loader
|
||||
LoaderCustom,
|
||||
};
|
||||
|
||||
20
constants/api-storage.ts
Normal file
20
constants/api-storage.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const API_IMAGE = {
|
||||
/**
|
||||
*
|
||||
* @param fileId | file id from wibu storage , atau bisa disimpan di DB
|
||||
* @param size | file size 10 - 1000 , tergantung ukuran file dan kebutuhan saar di tampilkan
|
||||
* @type {string}
|
||||
*/
|
||||
GET: ({ fileId, size }: { fileId: string; size?: string }) =>
|
||||
size
|
||||
? `https://wibu-storage.wibudev.com/api/files/${fileId}-size-${size}`
|
||||
: `https://wibu-storage.wibudev.com/api/files/${fileId}`,
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @returns alamat API dari wibu storage
|
||||
*/
|
||||
GET_NO_PARAMS: "https://wibu-storage.wibudev.com/api/files/",
|
||||
};
|
||||
|
||||
export default API_IMAGE;
|
||||
@@ -1,6 +1,7 @@
|
||||
const DUMMY_IMAGE = {
|
||||
avatar: require("@/assets/images/dummy/dummy-user.png"),
|
||||
background: require("@/assets/images/dummy/dummy-image-background.jpg"),
|
||||
dummy_image: require("@/assets/images/dummy/dummy-image.jpg"),
|
||||
};
|
||||
|
||||
export default DUMMY_IMAGE;
|
||||
|
||||
@@ -92,7 +92,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
await AsyncStorage.setItem("authToken", token);
|
||||
|
||||
const responseUser = await apiConfig.get(
|
||||
`/mobile/user?token=${token}`,
|
||||
`/mobile?token=${token}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,71 +1,73 @@
|
||||
export {listDataNotPublishInvesment, listDataPublishInvesment};
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
|
||||
const listDataNotPublishInvesment = [
|
||||
export { listDataNotPublishInvesment, listDataPublishInvesment };
|
||||
|
||||
const listDataNotPublishInvesment = ({ data }: { data: any }) => [
|
||||
{
|
||||
label: "Target Dana",
|
||||
value: "Rp. 7.500.000",
|
||||
value: `Rp. ${formatCurrencyDisplay(data?.targetDana) || "-"}`,
|
||||
},
|
||||
{
|
||||
label: "Harga Per Lembar",
|
||||
value: "Rp. 2.400",
|
||||
value: `Rp. ${formatCurrencyDisplay(data?.hargaLembar) || "-"}`,
|
||||
},
|
||||
{
|
||||
label: "Return Of Investment (ROI)",
|
||||
value: "3 %",
|
||||
value: `${data?.roi || "-"} %`,
|
||||
},
|
||||
{
|
||||
label: "Total Lembar",
|
||||
value: "1.200",
|
||||
},
|
||||
{
|
||||
label: "Jadwal Pembagian",
|
||||
value: "Rp. 2.880.000",
|
||||
},
|
||||
{
|
||||
label: "Pembagian Deviden",
|
||||
value: "Selamanya",
|
||||
value: data?.totalLembar || "-",
|
||||
},
|
||||
{
|
||||
label: "Pencarian Investor",
|
||||
value: "30 Hari",
|
||||
value: data && data?.MasterPencarianInvestor?.name + " hari" || "-",
|
||||
},
|
||||
{
|
||||
label: "Jadwal Pembagian",
|
||||
value: data && data?.MasterPembagianDeviden?.name + " bulan" || "-",
|
||||
},
|
||||
{
|
||||
label: "Pembagian Deviden",
|
||||
value: data?.MasterPeriodeDeviden?.name || "-",
|
||||
},
|
||||
];
|
||||
|
||||
const listDataPublishInvesment = [
|
||||
const listDataPublishInvesment = ({ data }: { data: any }) => [
|
||||
{
|
||||
label: "Investor",
|
||||
value: "10",
|
||||
value: data?.investor,
|
||||
},
|
||||
{
|
||||
label: "Target Dana",
|
||||
value: "Rp. 7.500.000",
|
||||
value: data?.targetDana,
|
||||
},
|
||||
{
|
||||
label: "Harga Per Lembar",
|
||||
value: "Rp. 2.400",
|
||||
value: data?.hargaPerLembar,
|
||||
},
|
||||
{
|
||||
label: "Return Of Investment (ROI)",
|
||||
value: "3 %",
|
||||
value: data?.roi + " %",
|
||||
},
|
||||
{
|
||||
label: "Total Lembar",
|
||||
value: "1.200",
|
||||
value: data?.totalLembar,
|
||||
},
|
||||
{
|
||||
label: "Sisa Lembar",
|
||||
value: "600",
|
||||
value: data?.sisaLembar,
|
||||
},
|
||||
{
|
||||
label: "Jadwal Pembagian",
|
||||
value: "Rp. 2.880.000",
|
||||
value: data?.jadwalPembagian,
|
||||
},
|
||||
{
|
||||
label: "Pembagian Deviden",
|
||||
value: "Selamanya",
|
||||
value: data?.pembagianDeviden,
|
||||
},
|
||||
{
|
||||
label: "Pencarian Investor",
|
||||
value: "30 Hari",
|
||||
value: data?.pencarianInvestor,
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
18107
package-lock.json
generated
18107
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user