Compare commits

..

1 Commits

Author SHA1 Message Date
821a211f58 Collaboration
Fix:
- Integrasi API: Beranda, create, list partisipan, check sudah berpartisipasi ?

### No Issue
2025-09-22 17:31:40 +08:00
11 changed files with 554 additions and 155 deletions

View File

@@ -125,7 +125,6 @@ export default function UserLayout() {
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="collaboration/[id]/edit"
options={{
@@ -133,6 +132,13 @@ export default function UserLayout() {
headerLeft: () => <BackButton />,
}}
/>
<Stack.Screen
name="collaboration/[id]/create-pacticipants"
options={{
title: "Ajukan Partisipasi",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== End Collaboration Section ========= */}

View File

@@ -1,8 +1,37 @@
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();
setListData(response.data);
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
}
};
return (
<>
<ViewWrapper
@@ -15,13 +44,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>
</>
);

View File

@@ -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>
);
}

View File

@@ -1,22 +1,73 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BackButton,
ButtonCustom,
DotButton,
DrawerCustom,
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();
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 +80,27 @@ export default function CollaborationDetail() {
}}
/>
<ViewWrapper>
<Collaboration_BoxDetailSection id={id as string} />
{!data && !isParticipant ? (
<LoaderCustom />
) : (
<>
<Collaboration_BoxDetailSection data={data} />
<ButtonCustom onPress={() => setOpenDrawerPartisipasi(true)}>
Partisipasi
</ButtonCustom>
<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 +111,8 @@ export default function CollaborationDetail() {
required
showCount
maxLength={500}
value={description}
onChangeText={setDescription}
/>
<ButtonCustom
@@ -58,19 +123,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={[

View File

@@ -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 data</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 />

View File

@@ -1,53 +1,175 @@
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);
console.log("[DATA]>>", newData);
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>
);
}

View File

@@ -1,3 +1,4 @@
import { AccentColor } from "@/constants/color-palet";
import Divider from "../Divider/Divider";
import Grid from "../Grid/GridCustom";
import AvatarComp from "../Image/AvatarComp";
@@ -39,7 +40,7 @@ const AvatarUsernameAndOtherComponent = ({
</Grid.Col>
)}
</Grid>
{withBottomLine && <Divider marginTop={0} />}
{withBottomLine && <Divider color={AccentColor.blue} marginTop={0} />}
</>
);
};

View File

@@ -1,22 +1,33 @@
import {
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
Grid,
StackCustom,
TextCustom
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
Grid,
Spacing,
StackCustom,
TextCustom,
} from "@/components";
export default function Collaboration_BoxDetailSection({ id }: { id: string }) {
export default function Collaboration_BoxDetailSection({
data,
}: {
data: any;
}) {
return (
<>
<BoxWithHeaderSection>
<AvatarUsernameAndOtherComponent
avatar={data?.Author?.Profile?.imageId}
name={data?.Author?.username}
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
withBottomLine
/>
<Spacing height={10}/>
<StackCustom>
<AvatarUsernameAndOtherComponent />
<TextCustom align="center" bold size="large">
Judul Proyek {id}
{data?.title || ""}
</TextCustom>
{listData.map((item, index) => (
{listData(data).map((item, index) => (
<Grid key={index}>
<Grid.Col span={4}>
<TextCustom bold>{item.title}</TextCustom>
@@ -32,23 +43,21 @@ export default function Collaboration_BoxDetailSection({ id }: { id: string }) {
);
}
const listData = [
const listData = (data: any) => [
{
title: "Industri",
value: "Pilihan Industri",
value: data?.ProjectCollaborationMaster_Industri?.name || "-",
},
{
title: "Deskripsi",
value: "Deskripsi Proyek",
title: "Lokasi",
value: data?.lokasi || "-",
},
{
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?.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?.benefit || "-",
},
];

View File

@@ -7,24 +7,12 @@ import {
import { Href } from "expo-router";
function Collaboration_BoxPublishSection({
id,
title,
username,
description,
href,
// Avatar
sourceAvatar,
data,
rightComponentAvatar,
}: {
id: string;
title?: string;
username?: string;
description?: string;
href: Href;
// Avatar
sourceAvatar?: string;
data: any;
rightComponentAvatar?: React.ReactNode;
}) {
return (
@@ -32,21 +20,18 @@ function Collaboration_BoxPublishSection({
<BoxWithHeaderSection href={href}>
<StackCustom gap={0}>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${id}`}
name={username || "Username"}
avatarHref={`/profile/${data?.Author?.id}`}
name={data?.Author?.username || "Username"}
rightComponent={rightComponentAvatar}
avatar={sourceAvatar as any}
avatar={data?.Author?.Profile?.imageId}
withBottomLine
/>
<StackCustom>
<TextCustom truncate={2} size="large" bold align="center">
{title || "Lorem ipsum dolor sit"}
</TextCustom>
<TextCustom truncate={2}>
{description ||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa, obcaecati quia suscipit numquam, voluptates commodi porro impedit natus quos doloremque!"}
{data?.title || "-"}
</TextCustom>
<TextCustom truncate={2}>{data?.purpose || "-"}</TextCustom>
{/* <TextCustom bold size="small" >
2 Partisipan
</TextCustom> */}

View File

@@ -0,0 +1,71 @@
import { apiConfig } from "../api-config";
export async function apiCollaborationCreate({ data }: { data: any }) {
try {
const response = await apiConfig.post(`/mobile/collaboration`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
export async function apiCollaborationGetAll() {
try {
const response = await apiConfig.get(`/mobile/collaboration`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiCollaborationGetOne({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/collaboration/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiCollaborationCreatePartisipasi({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.post(
`/mobile/collaboration/${id}/participants`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiCollaborationGetParticipants({
id,
category,
authorId,
}: {
id: string;
category: "list" | "check-participant";
authorId?: string;
}) {
try {
const authorQuery = authorId ? `&authorId=${authorId}` : "";
const response = await apiConfig.get(
`/mobile/collaboration/${id}/participants?category=${category}${authorQuery}`
);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -28,4 +28,13 @@ export async function apiMasterEventType() {
} catch (error) {
throw error;
}
}
}
export async function apiMasterCollaborationType() {
try {
const response = await apiConfig.get(`/mobile/master/collaboration-industry`);
return response.data;
} catch (error) {
throw error;
}
}