upd: dokumen divisi

Deskripsi:
- move dokumen divisi
- copy dokumen divisi
- share dokumen divisi

NO issues
This commit is contained in:
amel
2025-06-02 14:32:32 +08:00
parent 572db6f5d1
commit d9a9c821ea
7 changed files with 308 additions and 119 deletions

View File

@@ -8,9 +8,15 @@ import DrawerBottom from "@/components/drawerBottom";
import { InputForm } from "@/components/inputForm"; import { InputForm } from "@/components/inputForm";
import MenuItemRow from "@/components/menuItemRow"; import MenuItemRow from "@/components/menuItemRow";
import ModalFloat from "@/components/modalFloat"; import ModalFloat from "@/components/modalFloat";
import ModalSelectMultiple from "@/components/modalSelectMultiple";
import { ColorsStatus } from "@/constants/ColorsStatus"; import { ColorsStatus } from "@/constants/ColorsStatus";
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiDocumentDelete, apiDocumentRename, apiGetDocument } from "@/lib/api"; import {
apiDocumentDelete,
apiDocumentRename,
apiGetDocument,
apiShareDocument,
} from "@/lib/api";
import { setUpdateDokumen } from "@/lib/dokumenUpdate"; import { setUpdateDokumen } from "@/lib/dokumenUpdate";
import { useAuthSession } from "@/providers/AuthProvider"; import { useAuthSession } from "@/providers/AuthProvider";
import { import {
@@ -49,6 +55,7 @@ type PropsPath = {
}; };
export default function DocumentDivision() { export default function DocumentDivision() {
const [isShare, setShare] = useState(false);
const { token, decryptToken } = useAuthSession(); const { token, decryptToken } = useAuthSession();
const { id } = useLocalSearchParams<{ id: string }>(); const { id } = useLocalSearchParams<{ id: string }>();
const [path, setPath] = useState("home"); const [path, setPath] = useState("home");
@@ -61,7 +68,7 @@ export default function DocumentDivision() {
const [copyAllowed, setCopyAllowed] = useState(true); const [copyAllowed, setCopyAllowed] = useState(true);
const [modalMore, setModalMore] = useState(false); const [modalMore, setModalMore] = useState(false);
const [isRename, setRename] = useState(false); const [isRename, setRename] = useState(false);
const dispatch = useDispatch() const dispatch = useDispatch();
const update = useSelector((state: any) => state.dokumenUpdate); const update = useSelector((state: any) => state.dokumenUpdate);
const [bodyRename, setBodyRename] = useState({ const [bodyRename, setBodyRename] = useState({
id: "", id: "",
@@ -181,37 +188,67 @@ export default function DocumentDivision() {
async function handleRename() { async function handleRename() {
try { try {
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current));
const response = await apiDocumentRename({ user: hasil, ...bodyRename }) const response = await apiDocumentRename({ user: hasil, ...bodyRename });
if (response.success) { if (response.success) {
ToastAndroid.show("Berhasil mengubah nama", ToastAndroid.SHORT) ToastAndroid.show("Berhasil mengubah nama", ToastAndroid.SHORT);
dispatch(setUpdateDokumen(!update)) dispatch(setUpdateDokumen(!update));
handleBatal() handleBatal();
} else { } else {
ToastAndroid.show(response.message, ToastAndroid.SHORT) ToastAndroid.show(response.message, ToastAndroid.SHORT);
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error);
ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT) ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT);
} finally { } finally {
setRename(false) setRename(false);
} }
} }
async function handleDelete() { async function handleDelete() {
try { try {
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current));
const response = await apiDocumentDelete({ user: hasil, data: selectedFiles }) const response = await apiDocumentDelete({
user: hasil,
data: selectedFiles,
});
if (response.success) { if (response.success) {
ToastAndroid.show("Berhasil menghapus", ToastAndroid.SHORT) ToastAndroid.show("Berhasil menghapus", ToastAndroid.SHORT);
dispatch(setUpdateDokumen(!update)) dispatch(setUpdateDokumen(!update));
handleBatal() handleBatal();
} else { } else {
ToastAndroid.show(response.message, ToastAndroid.SHORT) ToastAndroid.show(response.message, ToastAndroid.SHORT);
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error);
ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT) ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT);
}
}
useEffect(() => {
handleBatal();
}, [update]);
async function handleShare(selectedDivision: any[]) {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiShareDocument({
user: hasil,
dataDivision: selectedDivision,
dataItem: selectedFiles,
});
if (response.success) {
ToastAndroid.show("Berhasil membagikan item", ToastAndroid.SHORT);
dispatch(setUpdateDokumen(!update));
handleBatal();
} else {
ToastAndroid.show(response.message, ToastAndroid.SHORT);
}
} catch (error) {
console.error(error);
ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT);
} finally {
setShare(false);
} }
} }
@@ -352,7 +389,7 @@ export default function DocumentDivision() {
title: "Konfirmasi", title: "Konfirmasi",
desc: "Apakah anda yakin ingin menghapus dokumen?", desc: "Apakah anda yakin ingin menghapus dokumen?",
onPress: () => { onPress: () => {
handleDelete() handleDelete();
}, },
}); });
}} }}
@@ -385,7 +422,9 @@ export default function DocumentDivision() {
/> />
} }
title="Bagikan" title="Bagikan"
onPress={() => { }} onPress={() => {
setShare(true);
}}
column="many" column="many"
color="white" color="white"
disabled={selectedFiles.length != 1 || shareSelected} disabled={selectedFiles.length != 1 || shareSelected}
@@ -434,7 +473,9 @@ export default function DocumentDivision() {
title="Ganti Nama" title="Ganti Nama"
isVisible={isRename} isVisible={isRename}
setVisible={setRename} setVisible={setRename}
onSubmit={() => { handleRename() }} onSubmit={() => {
handleRename();
}}
disableSubmit={bodyRename.name == ""} disableSubmit={bodyRename.name == ""}
> >
<View> <View>
@@ -448,6 +489,19 @@ export default function DocumentDivision() {
/> />
</View> </View>
</ModalFloat> </ModalFloat>
<ModalSelectMultiple
choose="dinas"
title="Bagikan"
category="share-division"
open={isShare}
close={setShare}
onSelect={(value) => {
handleShare(value)
}}
value={id}
item={selectedFiles[0]?.id}
/>
</SafeAreaView> </SafeAreaView>
); );
} }

View File

@@ -208,7 +208,7 @@ export default function MenuBottomSelectDocument({ onDone }: Props) {
setShare(false) setShare(false)
}} }}
/> />
<ModalSalinMove open={isMoveCopy} close={setMoveCopy} category={valMoveCopy} onConfirm={(value: string) => { }} /> <ModalSalinMove open={isMoveCopy} close={setMoveCopy} category={valMoveCopy} onConfirm={(value: string) => { }} dataChoose={[]} />
</> </>
) )
} }

View File

@@ -1,8 +1,9 @@
import Styles from "@/constants/Styles"; import Styles from "@/constants/Styles";
import { apiMoveDocument } from "@/lib/api"; import { apiCopyDocument, apiMoveDocument } from "@/lib/api";
import { setUpdateDokumen } from "@/lib/dokumenUpdate"; import { setUpdateDokumen } from "@/lib/dokumenUpdate";
import { useAuthSession } from "@/providers/AuthProvider"; import { useAuthSession } from "@/providers/AuthProvider";
import { MaterialCommunityIcons } from "@expo/vector-icons"; import { MaterialCommunityIcons } from "@expo/vector-icons";
import { useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { ToastAndroid, View } from "react-native"; import { ToastAndroid, View } from "react-native";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
@@ -22,90 +23,159 @@ type Props = {
createdBy: string; createdBy: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
} };
export default function ModalMore({ onClose, data, share }: { onClose: () => void, data: Props[], share: boolean }) { export default function ModalMore({
const [isInformasi, setInformasi] = useState(false) onClose,
const [isCut, setIsCut] = useState(false) data,
const [isCopy, setIsCopy] = useState(false) share,
const [forbidCopy, setForbidCopy] = useState(true) }: {
const [nFileSelected, setNFileSelected] = useState(0) onClose: () => void;
const { token, decryptToken } = useAuthSession() data: Props[];
const dispatch = useDispatch() share: boolean;
const update = useSelector((state: any) => state.dokumenUpdate) }) {
const [isMoveCopy, setMoveCopy] = useState(false) const { id } = useLocalSearchParams<{ id: string }>();
const [isInformasi, setInformasi] = useState(false);
const [isCut, setIsCut] = useState(false);
const [isCopy, setIsCopy] = useState(false);
const [forbidCopy, setForbidCopy] = useState(true);
const [nFileSelected, setNFileSelected] = useState(0);
const { token, decryptToken } = useAuthSession();
const dispatch = useDispatch();
const update = useSelector((state: any) => state.dokumenUpdate);
function cekFileSelected() { function cekFileSelected() {
const cek = data.some((i: any) => i.category == "FOLDER") const cek = data.some((i: any) => i.category == "FOLDER");
setForbidCopy(cek) setForbidCopy(cek);
setNFileSelected(data.length) setNFileSelected(data.length);
} }
useEffect(() => { useEffect(() => {
cekFileSelected() cekFileSelected();
}, [data]) }, [data]);
async function handleMove(path: string) { async function handleMove(path: string) {
try { try {
const hasil = await decryptToken(String(token?.current)) const hasil = await decryptToken(String(token?.current));
const response = await apiMoveDocument({ user: hasil, dataItem: data, path }) const response = await apiMoveDocument({
user: hasil,
dataItem: data,
path,
});
if (response.success) { if (response.success) {
ToastAndroid.show("Berhasil memindahkan file", ToastAndroid.SHORT) ToastAndroid.show("Berhasil memindahkan file", ToastAndroid.SHORT);
dispatch(setUpdateDokumen(!update)) dispatch(setUpdateDokumen(!update));
} else { } else {
ToastAndroid.show(response.message, ToastAndroid.SHORT) ToastAndroid.show(response.message, ToastAndroid.SHORT);
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error);
ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT) ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT);
} finally { } finally {
setMoveCopy(false) setIsCut(false);
onClose() onClose();
}
}
async function handleCopy(path: string) {
try {
const hasil = await decryptToken(String(token?.current));
const response = await apiCopyDocument({
user: hasil,
dataItem: data,
path,
idDivision: id
});
if (response.success) {
ToastAndroid.show("Berhasil menyalin file", ToastAndroid.SHORT);
dispatch(setUpdateDokumen(!update));
} else {
ToastAndroid.show(response.message, ToastAndroid.SHORT);
}
} catch (error) {
console.error(error);
ToastAndroid.show("Terjadi kesalahan", ToastAndroid.SHORT);
} finally {
setIsCopy(false);
onClose();
} }
} }
return ( return (
<> <>
<View style={Styles.rowItemsCenter}> <View style={Styles.rowItemsCenter}>
{ {!share && (
!share &&
<MenuItemRow <MenuItemRow
icon={<MaterialCommunityIcons name="folder-move-outline" color="black" size={25} />} icon={
<MaterialCommunityIcons
name="folder-move-outline"
color="black"
size={25}
/>
}
title="Pindah" title="Pindah"
onPress={() => { onPress={() => {
setMoveCopy(true) setIsCut(true);
}} }}
/> />
} )}
{ {!forbidCopy && (
!forbidCopy &&
<MenuItemRow <MenuItemRow
icon={<MaterialCommunityIcons name="folder-multiple-outline" color="black" size={25} />} icon={
<MaterialCommunityIcons
name="folder-multiple-outline"
color="black"
size={25}
/>
}
title="Salin" title="Salin"
onPress={() => { onPress={() => {
// handleMoveCopy('copy') setIsCopy(true);
}} }}
/> />
} )}
{ {nFileSelected == 1 && (
nFileSelected == 1 &&
<MenuItemRow <MenuItemRow
icon={<MaterialCommunityIcons name="information-variant" color="black" size={25} />} icon={
<MaterialCommunityIcons
name="information-variant"
color="black"
size={25}
/>
}
title="Informasi" title="Informasi"
onPress={() => { onPress={() => {
// onClose() setInformasi(true);
setInformasi(true)
}} }}
/> />
} )}
</View> </View>
<DrawerBottom animation="slide" isVisible={isInformasi} setVisible={setInformasi} title="Informasi Dokumen" height={80}> <DrawerBottom
animation="slide"
isVisible={isInformasi}
setVisible={setInformasi}
title="Informasi Dokumen"
height={80}
>
<ModalInformasi data={data[0]} /> <ModalInformasi data={data[0]} />
</DrawerBottom> </DrawerBottom>
<ModalSalinMove open={isMoveCopy} close={() => setMoveCopy(false)} category={'move'} onConfirm={(value: string) => handleMove(value)} /> <ModalSalinMove
open={isCut}
close={() => setIsCut(false)}
category={"move"}
onConfirm={(value: string) => handleMove(value)}
dataChoose={data}
/>
<ModalSalinMove
open={isCopy}
close={() => setIsCopy(false)}
category={"copy"}
onConfirm={(value: string) => handleCopy(value)}
dataChoose={data}
/>
</> </>
) );
} }

View File

@@ -13,6 +13,7 @@ type Props = {
close: (value: boolean) => void close: (value: boolean) => void
category: 'copy' | 'move' category: 'copy' | 'move'
onConfirm: (value: string) => void onConfirm: (value: string) => void
dataChoose: any[]
} }
type DataProps = { type DataProps = {
@@ -33,7 +34,7 @@ type PropsPath = {
name: string; name: string;
}; };
export default function ModalSalinMove({ open, close, category, onConfirm }: Props) { export default function ModalSalinMove({ open, close, category, onConfirm, dataChoose }: Props) {
const [data, setData] = useState<DataProps[]>([]) const [data, setData] = useState<DataProps[]>([])
const { token, decryptToken } = useAuthSession() const { token, decryptToken } = useAuthSession()
const [path, setPath] = useState('home') const [path, setPath] = useState('home')
@@ -79,18 +80,28 @@ export default function ModalSalinMove({ open, close, category, onConfirm }: Pro
</View> </View>
<View> <View>
{ {
data.map((item, index) => ( data.length > 0 ?
<BorderBottomItem data.map((item, index) => {
key={index} const found = dataChoose.some((i: any) => i.id == item.id);
borderType="bottom" return (
icon={<Ionicons name="folder-open-sharp" color={'#f9cc40'} size={30} />} <BorderBottomItem
title={item.name} key={index}
titleWeight="normal" borderType="bottom"
onPress={() => { icon={<Ionicons name="folder-open-sharp" style={[found ? Styles.cGray : Styles.cFolder]} size={30} />}
setPath(item.id); title={item.name}
}} titleWeight="normal"
/> onPress={() => {
)) if (found) return;
setPath(item.id);
}}
subtitle={found ? <Text style={[Styles.textInformation, Styles.cGray]}>Tidak dapat memilih folder ini</Text> : ''}
/>
)
}
)
:
<Text style={[Styles.textDefault, Styles.cGray, Styles.mt15, { textAlign: 'center' }]}>Tidak ada data</Text>
} }
</View> </View>
<View style={[Styles.rowOnly, Styles.mt15, Styles.absolute0]}> <View style={[Styles.rowOnly, Styles.mt15, Styles.absolute0]}>

View File

@@ -1,5 +1,5 @@
import Styles from "@/constants/Styles" import Styles from "@/constants/Styles"
import { apiGetDivisionGroup } from "@/lib/api" import { apiGetDivisionGroup, apiGetDocumentInformasi, apiGetListDivisionByIdDivision } from "@/lib/api"
import { useAuthSession } from "@/providers/AuthProvider" import { useAuthSession } from "@/providers/AuthProvider"
import { AntDesign } from "@expo/vector-icons" import { AntDesign } from "@expo/vector-icons"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
@@ -15,6 +15,7 @@ type Props = {
choose: string choose: string
onSelect: (value: any[]) => void onSelect: (value: any[]) => void
value?: any value?: any
item?: any
} }
type CheckedState = { type CheckedState = {
@@ -30,11 +31,12 @@ type GroupData = {
}[]; }[];
} }
export default function ModalSelectMultiple({ open, close, title, category, choose, onSelect, value }: Props) { export default function ModalSelectMultiple({ open, close, title, category, choose, onSelect, value, item }: Props) {
const [isChoose, setChoose] = useState(choose) const [isChoose, setChoose] = useState(choose)
const { token, decryptToken } = useAuthSession() const { token, decryptToken } = useAuthSession()
const [data, setData] = useState<any>([]) const [data, setData] = useState<any>([])
const [checked, setChecked] = useState<CheckedState>({}); const [checked, setChecked] = useState<CheckedState>({});
const [selectedDivision, setSelectedDivision] = useState<any>([]);
async function handleLoadChooseDivision() { async function handleLoadChooseDivision() {
try { try {
@@ -54,12 +56,27 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
} }
} }
useEffect(() => { async function handleLoadShareDivision() {
if (category == 'choose-division') { try {
handleLoadChooseDivision() const hasil = await decryptToken(String(token?.current))
} else if (category == 'share-division') { const response = await apiGetListDivisionByIdDivision({ user: hasil, search: '', division: value })
// handleLoadPosition() const response2 = await apiGetDocumentInformasi({ user: hasil, item: item, cat: 'share' })
setData(response.data.filter((i: any) => i.id != value))
setSelectedDivision(response2.data)
} catch (error) {
console.error(error)
} }
}
useEffect(() => {
if (open) {
if (category == 'choose-division') {
handleLoadChooseDivision()
} else if (category == 'share-division') {
handleLoadShareDivision()
}
}
}, [open]); }, [open]);
const handleCheck = (groupId: string, divisionId: string) => { const handleCheck = (groupId: string, divisionId: string) => {
@@ -93,19 +110,38 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
setChecked(newChecked); setChecked(newChecked);
}; };
const handleDivisionClick = (index: number) => {
if (selectedDivision.some((i: any) => i.id == data[index].id)) {
setSelectedDivision(
selectedDivision.filter((i: any) => i.id != data[index].id)
);
} else {
setSelectedDivision([
...selectedDivision,
{ id: data[index].id, name: data[index].name },
]);
}
};
const handleSubmit = () => { const handleSubmit = () => {
const selectedGroups: GroupData[] = []; if (category == "choose-division") {
Object.keys(checked).forEach((groupId) => { const selectedGroups: GroupData[] = [];
const group = data.find((item: { id: string }) => item.id === groupId); Object.keys(checked).forEach((groupId) => {
if (group) { const group = data.find((item: { id: string }) => item.id === groupId);
selectedGroups.push({ if (group) {
id: group.id, selectedGroups.push({
name: group.name, id: group.id,
Division: group.Division.filter((division: { id: string }) => checked[groupId].includes(division.id)), name: group.name,
}); Division: group.Division.filter((division: { id: string }) => checked[groupId].includes(division.id)),
} });
}); }
onSelect(selectedGroups); });
onSelect(selectedGroups);
} else {
onSelect(selectedDivision);
}
}; };
return ( return (
@@ -114,22 +150,22 @@ export default function ModalSelectMultiple({ open, close, title, category, choo
{ {
category == 'share-division' ? category == 'share-division' ?
<> <>
<Pressable style={[Styles.itemSelectModal]} onPress={() => { {
setChoose('dinas') data.map((item: any, index: number) => {
close(false) return (
}}> <Pressable key={index} style={[Styles.itemSelectModal]} onPress={() => {
<Text style={[Styles.textDefaultSemiBold]}>Sosial Kemasyarakatan</Text> handleDivisionClick(index)
<AntDesign name="check" size={20} /> }}>
</Pressable> <Text numberOfLines={1} style={[Styles.w80]}>{item.name}</Text>
{/* <Pressable style={[Styles.itemSelectModal]}> {
<Text>Kaur Pemerintahan</Text> selectedDivision.some((i: any) => i.id == item.id)
</Pressable> ? <AntDesign name="check" size={18} />
<Pressable style={[Styles.itemSelectModal]}> : <></>
<Text>Kasi Kemasyarakatan</Text> }
</Pressable> </Pressable>
<Pressable style={[Styles.itemSelectModal]}> )
<Text>PKK</Text> })
</Pressable> */} }
</> </>
: :
data.map((item: any, index: number) => { data.map((item: any, index: number) => {

View File

@@ -62,6 +62,9 @@ const Styles = StyleSheet.create({
cDefault: { cDefault: {
color: '#19345E' color: '#19345E'
}, },
cFolder:{
color: '#f9cc40'
},
mb05: { mb05: {
marginBottom: 5 marginBottom: 5
}, },

View File

@@ -399,6 +399,11 @@ export const apiGetDivisionMember = async ({ user, id, search }: { user: string,
return response.data; return response.data;
}; };
export const apiGetListDivisionByIdDivision = async ({ user, search, division }: { user: string, search: string, division: string }) => {
const response = await api.get(`mobile/division/more?user=${user}&search=${search}&division=${division}`);
return response.data;
};
export const apiCreateDivision = async (data: { data: { idGroup: string, name: string, desc: string }, member: [], admin: string[], user: string }) => { export const apiCreateDivision = async (data: { data: { idGroup: string, name: string, desc: string }, member: [], admin: string[], user: string }) => {
const response = await api.post(`/mobile/division`, data) const response = await api.post(`/mobile/division`, data)
return response.data; return response.data;
@@ -597,7 +602,6 @@ export const apiCreateFolderDocument = async (data: { name: string, path: string
}; };
export const apiUploadFileDocument = async ({ data }: { data: FormData }) => { export const apiUploadFileDocument = async ({ data }: { data: FormData }) => {
console.log(data)
const response = await api.post(`/mobile/document/upload`, data, const response = await api.post(`/mobile/document/upload`, data,
{ {
headers: { headers: {
@@ -612,3 +616,14 @@ export const apiMoveDocument = async (data: { path: string, dataItem: any[], use
const response = await api.post(`/mobile/document/more`, data) const response = await api.post(`/mobile/document/more`, data)
return response.data; return response.data;
}; };
export const apiCopyDocument = async (data: { path: string, dataItem: any[], user: string, idDivision: string }) => {
const response = await api.put(`/mobile/document/more`, data)
return response.data;
};
export const apiShareDocument = async (data: { dataDivision: any[], dataItem: any[], user: string }) => {
const response = await api.delete(`/mobile/document/more`, { data })
return response.data;
};