diff --git a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx
index 39f4029..f9f81fb 100644
--- a/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx
+++ b/app/(application)/division/[id]/(fitur-division)/calendar/[detail]/index.tsx
@@ -13,9 +13,10 @@ import { apiDeleteCalendarMember, apiGetCalendarOne, apiGetDivisionOneFeature }
import { setUpdateCalendar } from "@/lib/calendarUpdate"
import { useAuthSession } from "@/providers/AuthProvider"
import { MaterialCommunityIcons } from "@expo/vector-icons"
+import Clipboard from "@react-native-clipboard/clipboard"
import { router, Stack, useLocalSearchParams } from "expo-router"
import { useEffect, useState } from "react"
-import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
+import { Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
import Toast from "react-native-toast-message"
import { useDispatch, useSelector } from "react-redux"
@@ -115,6 +116,11 @@ export default function DetailEventCalendar() {
handleLoadMember();
}, [update.member]);
+ const handleCopy = (text: string) => {
+ Clipboard.setString(text);
+ Toast.show({ type: 'small', text1: 'Berhasil menyalin link', })
+ };
+
async function handleDeleteUser() {
try {
const hasil = await decryptToken(String(token?.current));
@@ -216,10 +222,14 @@ export default function DetailEventCalendar() {
loading ?
:
- {data?.linkMeet ? data.linkMeet : '-'}
+ data?.linkMeet ?
+ { handleCopy(data.linkMeet) }}>
+ {data.linkMeet}
+
+ : -
}
-
+
{
loading ?
diff --git a/app/(application)/division/[id]/(fitur-division)/document/index.tsx b/app/(application)/division/[id]/(fitur-division)/document/index.tsx
index 3668857..3f90895 100644
--- a/app/(application)/division/[id]/(fitur-division)/document/index.tsx
+++ b/app/(application)/division/[id]/(fitur-division)/document/index.tsx
@@ -65,6 +65,7 @@ type PropsPath = {
};
export default function DocumentDivision() {
+ const [loadingRename, setLoadingRename] = useState(false)
const [isShare, setShare] = useState(false)
const { token, decryptToken } = useAuthSession()
const { id } = useLocalSearchParams<{ id: string }>()
@@ -201,6 +202,7 @@ export default function DocumentDivision() {
async function handleRename() {
try {
+ setLoadingRename(true)
const hasil = await decryptToken(String(token?.current));
const response = await apiDocumentRename({ user: hasil, ...bodyRename });
if (response.success) {
@@ -214,7 +216,8 @@ export default function DocumentDivision() {
console.error(error);
Toast.show({ type: 'small', text1: 'Terjadi kesalahan', })
} finally {
- setRename(false);
+ setLoadingRename(false)
+ setRename(false)
}
}
@@ -360,25 +363,20 @@ export default function DocumentDivision() {
{
- loading ?
- arrSkeleton.map((item, index) => (
-
- ))
- :
- dataJalur.map((item, index) => (
- {
- setPath(item.id);
- }}
- >
- {item.id != "home" && (
-
- )}
- {item.name}
-
- ))
+ dataJalur.map((item, index) => (
+ {
+ setPath(item.id);
+ }}
+ >
+ {item.id != "home" && (
+
+ )}
+ {item.name}
+
+ ))
}
@@ -538,7 +536,7 @@ export default function DocumentDivision() {
isVisible={isRename}
setVisible={() => { setRename(false) }}
onSubmit={() => { handleRename() }}
- disableSubmit={bodyRename.name == ""}
+ disableSubmit={bodyRename.name == "" || loadingRename}
>
state.dokumenUpdate)
const [loading, setLoading] = useState(false)
+ const [loadingFolder, setLoadingFolder] = useState(false)
async function handleCreateFolder() {
try {
+ setLoadingFolder(true)
const hasil = await decryptToken(String(token?.current))
const response = await apiCreateFolderDocument({ data: { user: hasil, name, path, idDivision: id } })
if (response.success) {
@@ -39,6 +41,7 @@ export default function HeaderRightDocument({ path }: { path: string }) {
console.error(error)
Toast.show({ type: 'small', text1: 'Terjadi kesalahan', })
} finally {
+ setLoadingFolder(false)
setNewFolder(false)
}
}
@@ -148,7 +151,7 @@ export default function HeaderRightDocument({ path }: { path: string }) {
title="Buat Folder Baru"
isVisible={newFolder}
setVisible={() => { setNewFolder(false) }}
- disableSubmit={name == ""}
+ disableSubmit={name == "" || loadingFolder}
onSubmit={() => { handleCreateFolder() }}
>
diff --git a/components/document/modalNewFolder.tsx b/components/document/modalNewFolder.tsx
new file mode 100644
index 0000000..43927ac
--- /dev/null
+++ b/components/document/modalNewFolder.tsx
@@ -0,0 +1,65 @@
+import Styles from "@/constants/Styles";
+import { apiCreateFolderDocument } from "@/lib/api";
+import { useAuthSession } from "@/providers/AuthProvider";
+import { useLocalSearchParams } from "expo-router";
+import { useState } from "react";
+import { Pressable, View } from "react-native";
+import Toast from "react-native-toast-message";
+import Text from "../Text";
+import { InputForm } from "../inputForm";
+import ModalFloat from "../modalFloat";
+
+export function ModalNewFolder({ path, onCreated }: { path: string, onCreated: () => void }) {
+ const { token, decryptToken } = useAuthSession()
+ const [newFolder, setNewFolder] = useState(false)
+ const [name, setName] = useState("")
+ const [loadingFolder, setLoadingFolder] = useState(false)
+ const { id } = useLocalSearchParams<{ id: string }>();
+
+
+ async function handleCreateFolder() {
+ try {
+ setLoadingFolder(true)
+ const hasil = await decryptToken(String(token?.current))
+ const response = await apiCreateFolderDocument({ data: { user: hasil, name, path, idDivision: id } })
+ if (response.success) {
+ Toast.show({ type: 'small', text1: 'Berhasil membuat folder baru', })
+ } else {
+ Toast.show({ type: 'small', text1: response.message, })
+ }
+ } catch (error) {
+ console.error(error)
+ Toast.show({ type: 'small', text1: 'Terjadi kesalahan', })
+ } finally {
+ onCreated()
+ setLoadingFolder(false)
+ setNewFolder(false)
+ }
+ }
+
+ return (
+ <>
+ setNewFolder(true)}>
+ FOLDER BARU
+
+
+ { setNewFolder(false) }}
+ disableSubmit={name == "" || loadingFolder}
+ onSubmit={() => { handleCreateFolder() }}
+ >
+
+ { setName(value) }}
+ />
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/components/document/modalSalinMove.tsx b/components/document/modalSalinMove.tsx
index 670eb8e..01a4e96 100644
--- a/components/document/modalSalinMove.tsx
+++ b/components/document/modalSalinMove.tsx
@@ -8,6 +8,7 @@ import { Pressable, View } from "react-native"
import BorderBottomItem from "../borderBottomItem"
import DrawerBottom from "../drawerBottom"
import Text from "../Text"
+import { ModalNewFolder } from "./modalNewFolder"
type Props = {
open: boolean
@@ -106,9 +107,7 @@ export default function ModalSalinMove({ open, close, category, onConfirm, dataC
}
- close(false)}>
- BATAL
-
+ getData()} />
onConfirm(path)}>
{category == 'copy' ? 'SALIN' : 'PINDAH'}
diff --git a/components/imageWithLabel.tsx b/components/imageWithLabel.tsx
index dddfcfe..0bda9b4 100644
--- a/components/imageWithLabel.tsx
+++ b/components/imageWithLabel.tsx
@@ -11,7 +11,7 @@ type Props = {
export default function ImageWithLabel({ src, label, onClick }: Props) {
return (
-
+
{label}
diff --git a/components/inputDate.tsx b/components/inputDate.tsx
index 40f7710..f627855 100644
--- a/components/inputDate.tsx
+++ b/components/inputDate.tsx
@@ -28,7 +28,7 @@ type Props = {
export function InputDate({ label, value, placeholder, onChange, info, disable, error, errorText, required, mode, round, width, }: Props) {
const [modal, setModal] = useState(false);
const [valueFix, setValueFix] = useState(new Date())
- const [valueFirst, setValueFirst] = useState("")
+ const [valueFirst, setValueFirst] = useState(mode == "date" ? dayjs(new Date()).format("DD-MM-YYYY") : mode == "time" ? dayjs(new Date()).format("HH:mm") : "")
const onChangeDate = (type: string, selectedDate: any) => {
if (type === "set") {
@@ -45,6 +45,8 @@ export function InputDate({ label, value, placeholder, onChange, info, disable,
onChange(formatted)
setModal(false)
}
+ } else if (type === "dismissed") {
+ setModal(false)
}
};
@@ -100,11 +102,8 @@ export function InputDate({ label, value, placeholder, onChange, info, disable,
value={valueFix}
mode={mode}
display="spinner"
- onChange={(event, date) => {
- onChangeDate(event.type, date)
- }}
+ onChange={(event, date) => { onChangeDate(event.type, date) }}
onTouchCancel={() => setModal(false)}
-
/>
)
@@ -115,7 +114,7 @@ export function InputDate({ label, value, placeholder, onChange, info, disable,
mode={mode}
display="inline"
onChange={(event, date) => { onChangeDate(event.type, date) }}
- onTouchCancel={() => setModal(false)}
+ onTouchCancel={() => { setModal(false) }}
/>
)
)
diff --git a/components/position/headerRightPositionList.tsx b/components/position/headerRightPositionList.tsx
index 2e20af1..549623f 100644
--- a/components/position/headerRightPositionList.tsx
+++ b/components/position/headerRightPositionList.tsx
@@ -1,93 +1,19 @@
import Styles from "@/constants/Styles"
-import { apiCreatePosition } from "@/lib/api"
-import { setUpdatePosition } from "@/lib/positionSlice"
-import { useAuthSession } from "@/providers/AuthProvider"
import { AntDesign } from "@expo/vector-icons"
-import { useEffect, useState } from "react"
+import { useState } from "react"
import { View } from "react-native"
-import Toast from "react-native-toast-message"
-import { useDispatch, useSelector } from "react-redux"
-import { ButtonForm } from "../buttonForm"
+import { useSelector } from "react-redux"
import ButtonMenuHeader from "../buttonMenuHeader"
import DrawerBottom from "../drawerBottom"
-import { InputForm } from "../inputForm"
import MenuItemRow from "../menuItemRow"
import ModalFilter from "../modalFilter"
-import ModalSelect from "../modalSelect"
-import SelectForm from "../selectForm"
+import ModalFormCreatePosition from "./modalFormCreatePosition"
export default function HeaderRightPositionList() {
- const dispatch = useDispatch()
- const update = useSelector((state: any) => state.positionUpdate)
- const { token, decryptToken } = useAuthSession()
const entityUser = useSelector((state: any) => state.user)
const [isVisible, setVisible] = useState(false)
const [isVisibleTambah, setVisibleTambah] = useState(false)
const [isFilter, setFilter] = useState(false)
- const [isSelect, setSelect] = useState(false)
- const [choose, setChoose] = useState({ val: '', label: '' })
- const [disable, setDisable] = useState(true)
- const [dataForm, setDataForm] = useState({
- name: "",
- idGroup: "",
- })
- const [error, setError] = useState({
- name: false,
- idGroup: false
- });
-
- function validationForm(val: any, cat: 'name' | 'idGroup') {
- if (cat === 'name') {
- setDataForm({ ...dataForm, name: val })
- if (val == "") {
- setError({ ...error, name: true })
- } else {
- setError({ ...error, name: false })
- }
- } else if (cat === "idGroup") {
- setDataForm({ ...dataForm, idGroup: val })
- if (val == "") {
- setError({ ...error, idGroup: true })
- } else {
- setError({ ...error, idGroup: false })
- }
- }
- }
-
- function checkAll() {
- let nilai = false
- if (dataForm.name == "") {
- nilai = true
- }
-
- if ((entityUser.role == "supadmin" || entityUser.role == "developer") && (dataForm.idGroup == "" || String(dataForm.idGroup) == "null")) {
- nilai = true
- }
-
- setDisable(nilai)
- }
-
- useEffect(() => {
- checkAll()
- }, [dataForm])
-
- async function handleTambah() {
- try {
- setDisable(true)
- const hasil = await decryptToken(String(token?.current))
- const response = await apiCreatePosition({ user: hasil, name: dataForm.name, idGroup: dataForm.idGroup })
- dispatch(setUpdatePosition(!update))
- } catch (error) {
- console.error(error)
- } finally {
- setDisable(false)
- setVisibleTambah(false)
- setVisible(false)
- Toast.show({ type: 'small', text1: 'Berhasil menambahkan data', })
- }
-
- }
-
return (
<>
@@ -121,64 +47,13 @@ export default function HeaderRightPositionList() {
setVisibleTambah(false)} title="Tambah Jabatan">
-
-
- {
- (entityUser.role == 'supadmin' || entityUser.role == 'developer') &&
- {
- setVisibleTambah(false)
- setTimeout(() => {
- setSelect(true)
- }, 600)
- }}
- error={error.idGroup}
- errorText="Lembaga Desa harus diisi"
- />
- }
- { validationForm(value, 'name') }}
- error={error.name}
- errorText="Nama jabatan harus diisi"
- value={dataForm.name}
- />
-
-
- { handleTambah() }}
- disabled={disable} />
-
-
+ setVisibleTambah(false)} />
{
setFilter(false)
setVisible(false)
}} open={isFilter} page="position" />
-
- {
- validationForm(value.val, 'idGroup')
- setChoose(value)
- setSelect(false)
- setTimeout(() => {
- setVisibleTambah(true)
- }, 600)
- }}
- title="Lembaga Desa"
- open={isSelect}
- />
>
)
}
\ No newline at end of file
diff --git a/components/position/modalFormCreatePosition.tsx b/components/position/modalFormCreatePosition.tsx
new file mode 100644
index 0000000..256a3f3
--- /dev/null
+++ b/components/position/modalFormCreatePosition.tsx
@@ -0,0 +1,133 @@
+import Styles from "@/constants/Styles"
+import { apiCreatePosition } from "@/lib/api"
+import { setUpdatePosition } from "@/lib/positionSlice"
+import { useAuthSession } from "@/providers/AuthProvider"
+import { update } from "@react-native-firebase/database"
+import { useEffect, useState } from "react"
+import { View } from "react-native"
+import Toast from "react-native-toast-message"
+import { useDispatch, useSelector } from "react-redux"
+import { ButtonForm } from "../buttonForm"
+import { InputForm } from "../inputForm"
+import SelectForm from "../selectForm"
+import ModalSelect from "../modalSelect"
+
+export default function ModalFormCreatePosition({ onClose }: { onClose: () => void }) {
+ const dispatch = useDispatch()
+ const { token, decryptToken } = useAuthSession()
+ const entityUser = useSelector((state: any) => state.user)
+ const [choose, setChoose] = useState({ val: '', label: '' })
+ const [isSelect, setSelect] = useState(false)
+ const [disable, setDisable] = useState(true)
+ const [dataForm, setDataForm] = useState({
+ name: "",
+ idGroup: "",
+ })
+ const [error, setError] = useState({
+ name: false,
+ idGroup: false
+ });
+
+ function validationForm(val: any, cat: 'name' | 'idGroup') {
+ if (cat === 'name') {
+ setDataForm({ ...dataForm, name: val })
+ if (val == "") {
+ setError({ ...error, name: true })
+ } else {
+ setError({ ...error, name: false })
+ }
+ } else if (cat === "idGroup") {
+ setDataForm({ ...dataForm, idGroup: val })
+ if (val == "") {
+ setError({ ...error, idGroup: true })
+ } else {
+ setError({ ...error, idGroup: false })
+ }
+ }
+ }
+
+ function checkAll() {
+ let nilai = false
+ if (dataForm.name == "") {
+ nilai = true
+ }
+
+ if ((entityUser.role == "supadmin" || entityUser.role == "developer") && (dataForm.idGroup == "" || String(dataForm.idGroup) == "null")) {
+ nilai = true
+ }
+
+ setDisable(nilai)
+ }
+
+ useEffect(() => {
+ checkAll()
+ }, [dataForm])
+
+ async function handleTambah() {
+ try {
+ setDisable(true)
+ const hasil = await decryptToken(String(token?.current))
+ const response = await apiCreatePosition({ user: hasil, name: dataForm.name, idGroup: dataForm.idGroup })
+ dispatch(setUpdatePosition(!update))
+ } catch (error) {
+ console.error(error)
+ } finally {
+ setDisable(false)
+ Toast.show({ type: 'small', text1: 'Berhasil menambahkan data', })
+ onClose()
+ }
+
+ }
+
+ return (
+ <>
+
+
+ {
+ (entityUser.role == 'supadmin' || entityUser.role == 'developer') &&
+ {
+ setSelect(true)
+ }}
+ error={error.idGroup}
+ errorText="Lembaga Desa harus diisi"
+ />
+ }
+ { validationForm(value, 'name') }}
+ error={error.name}
+ errorText="Nama jabatan harus diisi"
+ value={dataForm.name}
+ />
+
+
+ { handleTambah() }}
+ disabled={disable} />
+
+
+
+ {
+ validationForm(value.val, 'idGroup')
+ setChoose(value)
+ setSelect(false)
+ }}
+ title="Lembaga Desa"
+ open={isSelect}
+ valChoose={choose.val}
+ />
+ >
+ )
+}
\ No newline at end of file
diff --git a/constants/Styles.ts b/constants/Styles.ts
index b76b1d3..726a5f1 100644
--- a/constants/Styles.ts
+++ b/constants/Styles.ts
@@ -94,6 +94,9 @@ const Styles = StyleSheet.create({
mv15: {
marginVertical: 15
},
+ mh03: {
+ marginHorizontal: 3
+ },
mh05: {
marginHorizontal: 5
},
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 3e509e4..a7e1e3f 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -2027,6 +2027,8 @@ PODS:
- React-utils (= 0.79.5)
- RNCAsyncStorage (2.1.2):
- React-Core
+ - RNCClipboard (1.16.3):
+ - React-Core
- RNDateTimePicker (8.4.1):
- React-Core
- RNFBApp (22.4.0):
@@ -2328,6 +2330,7 @@ DEPENDENCIES:
- ReactCodegen (from `build/generated/ios`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
+ - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)"
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
- "RNFBDatabase (from `../node_modules/@react-native-firebase/database`)"
@@ -2570,6 +2573,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
+ RNCClipboard:
+ :path: "../node_modules/@react-native-clipboard/clipboard"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNFBApp:
@@ -2712,6 +2717,7 @@ SPEC CHECKSUMS:
ReactCodegen: 272c9bc1a8a917bf557bd9d032a4b3e181c6abfe
ReactCommon: 7eb76fcd5133313d8c6a138a5c7dd89f80f189d5
RNCAsyncStorage: b9f5f78da5d16a853fe3dc22e8268d932fc45a83
+ RNCClipboard: f6679d470d0da2bce2a37b0af7b9e0bf369ecda5
RNDateTimePicker: 60f9e986d61e42169a2716c1b51f1f93dfa82665
RNFBApp: 12884d3bf9b3a0223efe4a0adce516edf72c4102
RNFBDatabase: 1e5c4bda4bb47a48820089ddef498f9af21cb52b
diff --git a/package.json b/package.json
index 815c340..31b5d7b 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@expo/vector-icons": "^14.0.2",
"@formatjs/intl-getcanonicallocales": "^2.5.5",
"@react-native-async-storage/async-storage": "2.1.2",
+ "@react-native-clipboard/clipboard": "^1.16.3",
"@react-native-community/cli": "^19.1.0",
"@react-native-community/datetimepicker": "8.4.1",
"@react-native-firebase/app": "^22.4.0",