From 1dea7a4421a1bf82ec099a9c0c7538cfd91266d0 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 13 May 2025 16:59:50 +0800 Subject: [PATCH] upd: project Deskripsi: - mengeluarkan anggota - link ke profile anggota - tambah anggota No Issues --- app/(application)/project/[id]/add-member.tsx | 166 ++++++++++++++++++ components/project/headerProjectDetail.tsx | 14 +- components/project/sectionMember.tsx | 46 +++-- lib/api.ts | 11 ++ 4 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 app/(application)/project/[id]/add-member.tsx diff --git a/app/(application)/project/[id]/add-member.tsx b/app/(application)/project/[id]/add-member.tsx new file mode 100644 index 0000000..6e44dab --- /dev/null +++ b/app/(application)/project/[id]/add-member.tsx @@ -0,0 +1,166 @@ +import ButtonBackHeader from "@/components/buttonBackHeader"; +import ButtonSaveHeader from "@/components/buttonSaveHeader"; +import ImageUser from "@/components/imageNew"; +import ImageWithLabel from "@/components/imageWithLabel"; +import InputSearch from "@/components/inputSearch"; +import Styles from "@/constants/Styles"; +import { apiAddMemberProject, apiGetProjectOne, apiGetUser } from "@/lib/api"; +import { setUpdateProject } from "@/lib/projectUpdate"; +import { useAuthSession } from "@/providers/AuthProvider"; +import { AntDesign } from "@expo/vector-icons"; +import { router, Stack, useLocalSearchParams } from "expo-router"; +import { useEffect, useState } from "react"; +import { Pressable, SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native"; +import { useDispatch, useSelector } from "react-redux"; + +type Props = { + idUser: string, + name: string, + img: string +} + +export default function AddMemberProject() { + const dispatch = useDispatch() + const update = useSelector((state: any) => state.projectUpdate) + const { token, decryptToken } = useAuthSession() + const { id } = useLocalSearchParams<{ id: string }>() + const [dataOld, setDataOld] = useState([]) + const [data, setData] = useState([]) + const [idGroup, setIdGroup] = useState('') + const [selectMember, setSelectMember] = useState([]) + const [search, setSearch] = useState('') + + async function handleLoad() { + try { + const hasil = await decryptToken(String(token?.current)) + const response = await apiGetProjectOne({ id: id, user: hasil, cat: 'member' }) + setDataOld(response.data) + const responseGroup = await apiGetProjectOne({ id: id, user: hasil, cat: 'data' }) + setIdGroup(responseGroup.data.idGroup) + const responsemember = await apiGetUser({ user: hasil, active: "true", search: search, group: String(responseGroup.data.idGroup) }) + setData(responsemember.data.filter((i: any) => i.idUserRole != 'supadmin')) + } catch (error) { + console.error(error) + } + } + + async function handleLoadMember(search: string) { + const hasil = await decryptToken(String(token?.current)) + const response = await apiGetUser({ user: hasil, active: "true", search: search, group: String(idGroup) }) + setData(response.data.filter((i: any) => i.idUserRole != 'supadmin')) + } + + + useEffect(() => { + handleLoad() + }, []); + + + function handleSearch(search: string) { + setSearch(search) + handleLoadMember(search) + } + + function onChoose(val: string, label: string, img?: string) { + if (selectMember.some((i: any) => i.idUser == val)) { + setSelectMember(selectMember.filter((i: any) => i.idUser != val)) + } else { + setSelectMember([...selectMember, { idUser: val, name: label, img }]) + } + } + + async function handleAddMember() { + try { + const hasil = await decryptToken(String(token?.current)) + const response = await apiAddMemberProject({ id: id, data: { user: hasil, member: selectMember } }) + if (response.success) { + ToastAndroid.show('Berhasil menambahkan anggota', ToastAndroid.SHORT) + dispatch(setUpdateProject({ ...update, member: !update.member })) + router.back() + } + } catch (error) { + console.error(error) + } + } + + + return ( + + { router.back() }} />, + headerTitle: 'Tambah Anggota Kegiatan', + headerTitleAlign: 'center', + headerRight: () => ( + 0 ? false : true} + onPress={() => { + handleAddMember() + }} + /> + ) + }} + /> + + handleSearch(val)} value={search} /> + + { + selectMember.length > 0 + ? + + + { + selectMember.map((item: any, index: any) => ( + onChoose(item.idUser, item.name, item.img)} + /> + )) + } + + + + : + Tidak ada member yang dipilih + } + + + { + data.length > 0 ? + data.map((item: any, index: any) => { + const found = dataOld.some((i: any) => i.idUser == item.id) + return ( + { + !found && onChoose(item.id, item.name, item.img) + }} + > + + + + {item.name} + { + found && sudah menjadi anggota + } + + + { + selectMember.some((i: any) => i.idUser == item.id) && + } + + ) + } + ) + : + Tidak ada data + } + + + + ) +} \ No newline at end of file diff --git a/components/project/headerProjectDetail.tsx b/components/project/headerProjectDetail.tsx index e23554a..b23957e 100644 --- a/components/project/headerProjectDetail.tsx +++ b/components/project/headerProjectDetail.tsx @@ -1,11 +1,11 @@ -import { useState } from "react" -import ButtonMenuHeader from "../buttonMenuHeader" -import DrawerBottom from "../drawerBottom" -import { View } from "react-native" import Styles from "@/constants/Styles" -import MenuItemRow from "../menuItemRow" import { AntDesign, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons" import { router } from "expo-router" +import { useState } from "react" +import { View } from "react-native" +import ButtonMenuHeader from "../buttonMenuHeader" +import DrawerBottom from "../drawerBottom" +import MenuItemRow from "../menuItemRow" type Props = { id: string | string[] @@ -40,8 +40,8 @@ export default function HeaderRightProjectDetail({ id }: Props) { icon={} title="Tambah Anggota" onPress={() => { - // setVisible(false) - + setVisible(false) + router.push(`/project/${id}/add-member`) }} /> diff --git a/components/project/sectionMember.tsx b/components/project/sectionMember.tsx index ae51638..978cde0 100644 --- a/components/project/sectionMember.tsx +++ b/components/project/sectionMember.tsx @@ -1,10 +1,12 @@ import Styles from "@/constants/Styles"; -import { apiGetProjectOne } from "@/lib/api"; +import { apiDeleteProjectMember, apiGetProjectOne } from "@/lib/api"; +import { setUpdateProject } from "@/lib/projectUpdate"; import { useAuthSession } from "@/providers/AuthProvider"; import { MaterialCommunityIcons } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; import { Text, ToastAndroid, View } from "react-native"; +import { useDispatch, useSelector } from "react-redux"; import AlertKonfirmasi from "../alertKonfirmasi"; import BorderBottomItem from "../borderBottomItem"; import DrawerBottom from "../drawerBottom"; @@ -21,10 +23,17 @@ type Props = { }; export default function SectionMember() { + const dispatch = useDispatch() + const update = useSelector((state: any) => state.projectUpdate) const [isModal, setModal] = useState(false); const { token, decryptToken } = useAuthSession(); const { id } = useLocalSearchParams<{ id: string }>(); const [data, setData] = useState([]); + const [memberChoose, setMemberChoose] = useState({ + id: '', + name: '', + }) + async function handleLoad() { try { @@ -42,7 +51,24 @@ export default function SectionMember() { useEffect(() => { handleLoad(); - }, []); + }, [update.member]); + + async function handleDeleteMember() { + try { + const hasil = await decryptToken(String(token?.current)); + const response = await apiDeleteProjectMember({ + user: hasil, + idUser: memberChoose.id, + }, id) + if (response.success) { + ToastAndroid.show("Berhasil menghapus anggota", ToastAndroid.SHORT); + dispatch(setUpdateProject({ ...update, member: !update.progress })) + setModal(false); + } + } catch (error) { + console.error(error); + } + } return ( <> @@ -66,6 +92,10 @@ export default function SectionMember() { subtitle={item.position} rightTopInfo="Anggota" onPress={() => { + setMemberChoose({ + id: item.idUser, + name: item.name, + }) setModal(true); }} /> @@ -81,7 +111,7 @@ export default function SectionMember() { animation="slide" isVisible={isModal} setVisible={setModal} - title="Menu" + title={memberChoose.name} > { setModal(false); - router.push("/member/123"); + router.push(`/member/${memberChoose.id}`); }} /> @@ -112,13 +142,7 @@ export default function SectionMember() { AlertKonfirmasi({ title: "Konfirmasi", desc: "Apakah Anda yakin ingin mengeluarkan anggota?", - onPress: () => { - setModal(false); - ToastAndroid.show( - "Berhasil mengeluarkan anggota", - ToastAndroid.SHORT - ); - }, + onPress: () => { handleDeleteMember() }, }); }} /> diff --git a/lib/api.ts b/lib/api.ts index e66248b..b1ef0c8 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -276,4 +276,15 @@ export const apiGetProjectTask = async ({ user, id }: { user: string, id: string export const apiEditProjectTask = async ({ data, id }: { data: { title: string, dateStart: string, user: string, dateEnd: string }, id: string }) => { const response = await api.post(`/mobile/project/detail/${id}`, data) return response.data; +}; + +export const apiDeleteProjectMember = async (data: { user: string, idUser: string }, id: string) => { + const response = await api.delete(`mobile/project/${id}/member`, { data }) + return response.data +}; + + +export const apiAddMemberProject = async ({ data, id }: { data: { user: string, member: any[] }, id: string }) => { + const response = await api.post(`/mobile/project/${id}/member`, data) + return response.data; }; \ No newline at end of file