upd: task division
Deskripsi: - tambah task division - hapus task division yg telah dibatalkan - akses user sesuai dengan role No Issuese
This commit is contained in:
@@ -14,7 +14,7 @@ export default function TaskDivisionCancel() {
|
|||||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||||
const { token, decryptToken } = useAuthSession();
|
const { token, decryptToken } = useAuthSession();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const update = useSelector((state: any) => state.projectUpdate);
|
const update = useSelector((state: any) => state.taskUpdate);
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState("");
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [disable, setDisable] = useState(false);
|
const [disable, setDisable] = useState(false);
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export default function DetailTaskDivision() {
|
|||||||
handleLoad()
|
handleLoad()
|
||||||
}, [update.progress, update.data])
|
}, [update.progress, update.data])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -58,7 +57,7 @@ export default function DetailTaskDivision() {
|
|||||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||||
headerTitle: loading ? 'Loading...' : data?.title,
|
headerTitle: loading ? 'Loading...' : data?.title,
|
||||||
headerTitleAlign: 'center',
|
headerTitleAlign: 'center',
|
||||||
headerRight: () => <HeaderRightTaskDetail id={detail} />,
|
headerRight: () => <HeaderRightTaskDetail id={detail} division={id} status={data?.status}/>,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
|||||||
@@ -1,47 +1,220 @@
|
|||||||
|
import BorderBottomItem from "@/components/borderBottomItem";
|
||||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||||
import ButtonSelect from "@/components/buttonSelect";
|
import ButtonSelect from "@/components/buttonSelect";
|
||||||
|
import DrawerBottom from "@/components/drawerBottom";
|
||||||
|
import ImageUser from "@/components/imageNew";
|
||||||
import { InputForm } from "@/components/inputForm";
|
import { InputForm } from "@/components/inputForm";
|
||||||
|
import MenuItemRow from "@/components/menuItemRow";
|
||||||
|
import ModalSelect from "@/components/modalSelect";
|
||||||
|
import SectionListAddTask from "@/components/project/sectionListAddTask";
|
||||||
import Styles from "@/constants/Styles";
|
import Styles from "@/constants/Styles";
|
||||||
|
import { apiCreateTask } from "@/lib/api";
|
||||||
|
import { setMemberChoose } from "@/lib/memberChoose";
|
||||||
|
import { setTaskCreate } from "@/lib/taskCreate";
|
||||||
|
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||||
|
import { useAuthSession } from "@/providers/AuthProvider";
|
||||||
|
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||||
|
import * as DocumentPicker from "expo-document-picker";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||||
import { SafeAreaView, ScrollView, ToastAndroid, View } from "react-native";
|
import { useEffect, useState } from "react";
|
||||||
|
import { SafeAreaView, ScrollView, Text, ToastAndroid, View } from "react-native";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
|
||||||
export default function CreateTaskDivision() {
|
export default function CreateTaskDivision() {
|
||||||
const { id } = useLocalSearchParams()
|
const { id } = useLocalSearchParams();
|
||||||
|
const { token, decryptToken } = useAuthSession();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isSelect, setSelect] = useState(false);
|
||||||
|
const [valChoose, setValChoose] = useState("");
|
||||||
|
const entitiesMember = useSelector((state: any) => state.memberChoose);
|
||||||
|
const taskCreate = useSelector((state: any) => state.taskCreate);
|
||||||
|
const update = useSelector((state: any) => state.taskUpdate)
|
||||||
|
const [fileForm, setFileForm] = useState<any[]>([])
|
||||||
|
const [indexDelFile, setIndexDelFile] = useState<number>(0)
|
||||||
|
const [title, setTitle] = useState('')
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [isModal, setModal] = useState(false)
|
||||||
|
let hitung = 0;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hitung == 0) {
|
||||||
|
dispatch(setTaskCreate([]));
|
||||||
|
dispatch(setMemberChoose([]));
|
||||||
|
}
|
||||||
|
hitung++;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
const pickDocumentAsync = async () => {
|
||||||
|
let result = await DocumentPicker.getDocumentAsync({
|
||||||
|
type: ["*/*"],
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
if (!result.canceled) {
|
||||||
|
if (result.assets[0].uri) {
|
||||||
|
setFileForm([...fileForm, result.assets[0]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function deleteFile(index: number) {
|
||||||
|
setFileForm([...fileForm.filter((val, i) => i !== index)])
|
||||||
|
setModal(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleBack() {
|
||||||
|
dispatch(setTaskCreate([]));
|
||||||
|
dispatch(setMemberChoose([]));
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCreate() {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current))
|
||||||
|
const fd = new FormData()
|
||||||
|
|
||||||
|
for (let i = 0; i < fileForm.length; i++) {
|
||||||
|
fd.append(`file${i}`, {
|
||||||
|
uri: fileForm[i].uri,
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
name: fileForm[i].name,
|
||||||
|
} as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
fd.append("data", JSON.stringify(
|
||||||
|
{ user: hasil, task: taskCreate, member: entitiesMember, title, idDivision: id }
|
||||||
|
))
|
||||||
|
|
||||||
|
const response = await apiCreateTask(fd)
|
||||||
|
if (response.success) {
|
||||||
|
dispatch(setUpdateTask({ ...update, data: !update.data }))
|
||||||
|
ToastAndroid.show('Berhasil menambahkan data', ToastAndroid.SHORT)
|
||||||
|
handleBack()
|
||||||
|
} else {
|
||||||
|
ToastAndroid.show(response.message, ToastAndroid.SHORT)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
headerLeft: () => (
|
||||||
|
<ButtonBackHeader
|
||||||
|
onPress={() => {
|
||||||
|
handleBack();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
headerTitle: `Tambah Tugas`,
|
headerTitle: `Tambah Tugas`,
|
||||||
headerTitleAlign: 'center',
|
headerTitleAlign: "center",
|
||||||
headerRight: () => <ButtonSaveHeader category="create" onPress={() => {
|
headerRight: () => (
|
||||||
ToastAndroid.show('Berhasil menambah data', ToastAndroid.SHORT)
|
<ButtonSaveHeader
|
||||||
router.push('../task?status=0')
|
disable={title == "" || entitiesMember.length == 0 || taskCreate.length == 0}
|
||||||
}} />
|
category="create"
|
||||||
|
onPress={() => { handleCreate() }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={[Styles.p15, Styles.mb100]}>
|
<View style={[Styles.p15, Styles.mb100]}>
|
||||||
<InputForm label="Judul Tugas" type="default" placeholder="Judul Tugas" required />
|
<InputForm
|
||||||
<ButtonSelect value="Tambah Tanggal & Tugas" />
|
label="Judul Tugas"
|
||||||
<ButtonSelect value="Upload File" />
|
type="default"
|
||||||
<ButtonSelect value="Tambah Anggota" />
|
placeholder="Judul Tugas"
|
||||||
{/* <ButtonForm
|
required
|
||||||
text="SIMPAN"
|
value={title}
|
||||||
onPress={() => {
|
onChange={(val) => {
|
||||||
AlertKonfirmasi({
|
setTitle(val);
|
||||||
title: 'Konfirmasi',
|
val == "" || val == "null" ? setError(true) : setError(false);
|
||||||
desc: 'Apakah anda yakin ingin menambahkan data?',
|
}}
|
||||||
onPress: () => {
|
error={error}
|
||||||
ToastAndroid.show('Berhasil menambahkan data', ToastAndroid.SHORT)
|
errorText="Judul Tugas tidak boleh kosong"
|
||||||
router.push('../task?status=0')
|
/>
|
||||||
}
|
<ButtonSelect value="Tambah Tanggal & Tugas" onPress={() => { router.push(`/division/${id}/task/create/task`); }} />
|
||||||
})
|
<ButtonSelect value="Upload File" onPress={pickDocumentAsync} />
|
||||||
}} /> */}
|
<ButtonSelect value="Tambah Anggota" onPress={() => { router.push(`/division/${id}/task/create/member`); }} />
|
||||||
|
<SectionListAddTask />
|
||||||
|
{
|
||||||
|
fileForm.length > 0 && (
|
||||||
|
<View style={[Styles.mb15]}>
|
||||||
|
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
|
||||||
|
<View style={[Styles.wrapPaper]}>
|
||||||
|
{
|
||||||
|
fileForm.map((item, index) => (
|
||||||
|
<BorderBottomItem
|
||||||
|
key={index}
|
||||||
|
borderType="all"
|
||||||
|
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||||
|
title={item.name}
|
||||||
|
titleWeight="normal"
|
||||||
|
onPress={() => { setIndexDelFile(index); setModal(true) }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{entitiesMember.length > 0 && (
|
||||||
|
<View>
|
||||||
|
<View style={[Styles.rowSpaceBetween, Styles.mv05]}>
|
||||||
|
<Text>Anggota</Text>
|
||||||
|
<Text>Total {entitiesMember.length} Anggota</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||||
|
{entitiesMember.map(
|
||||||
|
(item: { img: any; name: any }, index: any) => {
|
||||||
|
return (
|
||||||
|
<BorderBottomItem
|
||||||
|
key={index}
|
||||||
|
borderType="bottom"
|
||||||
|
icon={
|
||||||
|
<ImageUser
|
||||||
|
src={`https://wibu-storage.wibudev.com/api/files/${item.img}`}
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
title={item.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title="Menu">
|
||||||
|
<View style={Styles.rowItemsCenter}>
|
||||||
|
<MenuItemRow
|
||||||
|
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||||
|
title="Hapus"
|
||||||
|
onPress={() => { deleteFile(indexDelFile) }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</DrawerBottom>
|
||||||
|
|
||||||
|
<ModalSelect
|
||||||
|
category={"member"}
|
||||||
|
close={setSelect}
|
||||||
|
onSelect={(value) => { }}
|
||||||
|
title={"Pilih Anggota"}
|
||||||
|
open={isSelect}
|
||||||
|
idParent={''}
|
||||||
|
valChoose={valChoose}
|
||||||
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
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 { apiGetDivisionMember } from "@/lib/api";
|
||||||
|
import { setMemberChoose } from "@/lib/memberChoose";
|
||||||
|
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 AddMemberCreateTask() {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const { token, decryptToken } = useAuthSession()
|
||||||
|
const { id } = useLocalSearchParams<{ id: string, detail: string }>()
|
||||||
|
const [dataOld, setDataOld] = useState<Props[]>([])
|
||||||
|
const [data, setData] = useState<Props[]>([])
|
||||||
|
const [selectMember, setSelectMember] = useState<any[]>([])
|
||||||
|
const [search, setSearch] = useState('')
|
||||||
|
const entitiesMember = useSelector((state: any) => state.memberChoose)
|
||||||
|
|
||||||
|
async function handleLoadMemberDivision() {
|
||||||
|
const hasil = await decryptToken(String(token?.current))
|
||||||
|
const responMemberDivision = await apiGetDivisionMember({ id: id, user: hasil, search: search })
|
||||||
|
setData(responMemberDivision.data)
|
||||||
|
if (entitiesMember.length > 0) {
|
||||||
|
setSelectMember(entitiesMember)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleLoadMemberDivision()
|
||||||
|
}, [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 {
|
||||||
|
dispatch(setMemberChoose(selectMember))
|
||||||
|
router.back()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
ToastAndroid.show('Gagal menambahkan anggota', ToastAndroid.SHORT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||||
|
headerTitle: 'Pilih Anggota',
|
||||||
|
headerTitleAlign: 'center',
|
||||||
|
headerRight: () => (
|
||||||
|
<ButtonSaveHeader
|
||||||
|
category="create"
|
||||||
|
disable={selectMember.length > 0 ? false : true}
|
||||||
|
onPress={() => {
|
||||||
|
handleAddMember()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View style={[Styles.p15]}>
|
||||||
|
<InputSearch onChange={(val) => setSearch(val)} value={search} />
|
||||||
|
|
||||||
|
{
|
||||||
|
selectMember.length > 0
|
||||||
|
?
|
||||||
|
<View>
|
||||||
|
<ScrollView horizontal style={[Styles.mb10, Styles.pv10]}>
|
||||||
|
{
|
||||||
|
selectMember.map((item: any, index: any) => (
|
||||||
|
<ImageWithLabel
|
||||||
|
key={index}
|
||||||
|
label={item.name}
|
||||||
|
src={`https://wibu-storage.wibudev.com/api/files/${item.img}`}
|
||||||
|
onClick={() => onChoose(item.idUser, item.name, item.img)}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
:
|
||||||
|
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||||
|
}
|
||||||
|
<ScrollView>
|
||||||
|
|
||||||
|
{
|
||||||
|
data.length > 0 ?
|
||||||
|
data.map((item: any, index: any) => {
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
key={index}
|
||||||
|
style={[Styles.itemSelectModal]}
|
||||||
|
onPress={() => {
|
||||||
|
onChoose(item.idUser, item.name, item.img)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={[Styles.rowItemsCenter]}>
|
||||||
|
<ImageUser src={`https://wibu-storage.wibudev.com/api/files/${item.img}`} border />
|
||||||
|
<View style={[Styles.ml10]}>
|
||||||
|
<Text style={[Styles.textDefault]}>{item.name}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{
|
||||||
|
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} />
|
||||||
|
}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
:
|
||||||
|
<Text style={[Styles.textDefault, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||||
|
}
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||||
|
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||||
|
import { InputForm } from "@/components/inputForm";
|
||||||
|
import Styles from "@/constants/Styles";
|
||||||
|
import { setTaskCreate } from "@/lib/taskCreate";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { router, Stack } from "expo-router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
SafeAreaView,
|
||||||
|
ScrollView,
|
||||||
|
Text,
|
||||||
|
View
|
||||||
|
} from "react-native";
|
||||||
|
import DateTimePicker, {
|
||||||
|
DateType
|
||||||
|
} from "react-native-ui-datepicker";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
export default function CreateTaskAddTugas() {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const [disable, setDisable] = useState(true);
|
||||||
|
const [range, setRange] = useState<{
|
||||||
|
startDate: DateType;
|
||||||
|
endDate: DateType;
|
||||||
|
}>({ startDate: undefined, endDate: undefined });
|
||||||
|
const [error, setError] = useState({
|
||||||
|
startDate: false,
|
||||||
|
endDate: false,
|
||||||
|
title: false,
|
||||||
|
})
|
||||||
|
const [title, setTitle] = useState('');
|
||||||
|
const taskCreate = useSelector((state: any) => state.taskCreate)
|
||||||
|
|
||||||
|
const from = range.startDate
|
||||||
|
? dayjs(range.startDate).format("DD-MM-YYYY")
|
||||||
|
: "";
|
||||||
|
const to = range.endDate ? dayjs(range.endDate).format("DD-MM-YYYY") : "";
|
||||||
|
|
||||||
|
function checkAll() {
|
||||||
|
if (from == "" || to == "" || title == "" || title == "null" || error.startDate || error.endDate || error.title) {
|
||||||
|
setDisable(true)
|
||||||
|
} else {
|
||||||
|
setDisable(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onValidation(cat: string, val: string) {
|
||||||
|
if (cat == "title") {
|
||||||
|
setTitle(val)
|
||||||
|
if (val == "" || val == "null") {
|
||||||
|
setError(error => ({ ...error, title: true }))
|
||||||
|
} else {
|
||||||
|
setError(error => ({ ...error, title: false }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkAll()
|
||||||
|
}, [from, to, title, error])
|
||||||
|
|
||||||
|
async function handleCreate() {
|
||||||
|
try {
|
||||||
|
dispatch(setTaskCreate([...taskCreate, {
|
||||||
|
title: title,
|
||||||
|
dateStart: from,
|
||||||
|
dateEnd: to,
|
||||||
|
dateStartFix: dayjs(range.startDate).format("YYYY-MM-DD"),
|
||||||
|
dateEndFix: dayjs(range.endDate).format("YYYY-MM-DD"),
|
||||||
|
}]))
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
headerLeft: () => (
|
||||||
|
<ButtonBackHeader
|
||||||
|
onPress={() => {
|
||||||
|
router.back();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
headerTitle: "Tambah Tugas",
|
||||||
|
headerTitleAlign: "center",
|
||||||
|
headerRight: () => (
|
||||||
|
<ButtonSaveHeader
|
||||||
|
disable={disable}
|
||||||
|
category="create"
|
||||||
|
onPress={() => { handleCreate() }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ScrollView>
|
||||||
|
<View style={[Styles.p15, Styles.mb100]}>
|
||||||
|
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||||
|
<DateTimePicker
|
||||||
|
mode="range"
|
||||||
|
startDate={range.startDate}
|
||||||
|
endDate={range.endDate}
|
||||||
|
onChange={(param) => setRange(param)}
|
||||||
|
styles={{
|
||||||
|
selected: Styles.selectedDate,
|
||||||
|
selected_label: Styles.cWhite,
|
||||||
|
range_fill: Styles.selectRangeDate,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={[Styles.mv10]}>
|
||||||
|
<View style={[Styles.rowSpaceBetween]}>
|
||||||
|
<View style={[{ width: "48%" }]}>
|
||||||
|
<Text style={[Styles.mb05]}>
|
||||||
|
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||||
|
</Text>
|
||||||
|
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||||
|
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={[{ width: "48%" }]}>
|
||||||
|
<Text style={[Styles.mb05]}>
|
||||||
|
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||||
|
</Text>
|
||||||
|
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||||
|
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{
|
||||||
|
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
<InputForm
|
||||||
|
label="Judul Tugas"
|
||||||
|
type="default"
|
||||||
|
placeholder="Judul Tugas"
|
||||||
|
required
|
||||||
|
bg="white"
|
||||||
|
value={title}
|
||||||
|
error={error.title}
|
||||||
|
errorText="Judul tidak boleh kosong"
|
||||||
|
onChange={(e) => {
|
||||||
|
onValidation("title", e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Pressable, SafeAreaView, ScrollView, Text, View } from "react-native";
|
import { Pressable, SafeAreaView, ScrollView, Text, View } from "react-native";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -32,6 +33,7 @@ export default function ListTask() {
|
|||||||
const { token, decryptToken } = useAuthSession();
|
const { token, decryptToken } = useAuthSession();
|
||||||
const [data, setData] = useState<Props[]>([]);
|
const [data, setData] = useState<Props[]>([]);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const update = useSelector((state: any) => state.taskUpdate)
|
||||||
|
|
||||||
async function handleLoad() {
|
async function handleLoad() {
|
||||||
try {
|
try {
|
||||||
@@ -50,7 +52,7 @@ export default function ListTask() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleLoad();
|
handleLoad();
|
||||||
}, [status, search]);
|
}, [status, search, update.data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
|
|||||||
@@ -1,69 +1,165 @@
|
|||||||
import Styles from "@/constants/Styles"
|
import Styles from "@/constants/Styles"
|
||||||
import { AntDesign, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
|
import { apiDeleteTask, apiGetDivisionOneFeature } from "@/lib/api"
|
||||||
|
import { setUpdateTask } from "@/lib/taskUpdate"
|
||||||
|
import { useAuthSession } from "@/providers/AuthProvider"
|
||||||
|
import { AntDesign, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
|
||||||
import { router } from "expo-router"
|
import { router } from "expo-router"
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { View } from "react-native"
|
import { ToastAndroid, View } from "react-native"
|
||||||
|
import { useDispatch, useSelector } from "react-redux"
|
||||||
|
import AlertKonfirmasi from "../alertKonfirmasi"
|
||||||
import ButtonMenuHeader from "../buttonMenuHeader"
|
import ButtonMenuHeader from "../buttonMenuHeader"
|
||||||
import DrawerBottom from "../drawerBottom"
|
import DrawerBottom from "../drawerBottom"
|
||||||
import MenuItemRow from "../menuItemRow"
|
import MenuItemRow from "../menuItemRow"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string | string[]
|
id: string | string[]
|
||||||
|
division: string
|
||||||
|
status: number | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeaderRightTaskDetail({ id }: Props) {
|
export default function HeaderRightTaskDetail({ id, division, status }: Props) {
|
||||||
|
const { token, decryptToken } = useAuthSession()
|
||||||
const [isVisible, setVisible] = useState(false)
|
const [isVisible, setVisible] = useState(false)
|
||||||
|
const entityUser = useSelector((state: any) => state.user);
|
||||||
|
const [isMemberDivision, setIsMemberDivision] = useState(false);
|
||||||
|
const [isAdminDivision, setIsAdminDivision] = useState(false);
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const update = useSelector((state: any) => state.taskUpdate)
|
||||||
|
|
||||||
|
async function handleCheckMember() {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current));
|
||||||
|
const response = await apiGetDivisionOneFeature({
|
||||||
|
id: division,
|
||||||
|
user: hasil,
|
||||||
|
cat: "check-member",
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsMemberDivision(response.data);
|
||||||
|
|
||||||
|
const response2 = await apiGetDivisionOneFeature({
|
||||||
|
id: division,
|
||||||
|
user: hasil,
|
||||||
|
cat: "check-admin",
|
||||||
|
});
|
||||||
|
setIsAdminDivision(response2.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleCheckMember()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
async function handleDelete() {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current))
|
||||||
|
const response = await apiDeleteTask({ user: hasil }, String(id))
|
||||||
|
if (response.success) {
|
||||||
|
dispatch(setUpdateTask({ ...update, data: !update.data }))
|
||||||
|
ToastAndroid.show('Berhasil menghapus tugas', ToastAndroid.SHORT)
|
||||||
|
router.back()
|
||||||
|
} else {
|
||||||
|
ToastAndroid.show(response.message, ToastAndroid.SHORT)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
setVisible(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
|
{
|
||||||
|
(entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision
|
||||||
|
? <></>
|
||||||
|
:
|
||||||
|
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
|
||||||
|
}
|
||||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
||||||
<View style={Styles.rowItemsCenter}>
|
<View style={Styles.rowItemsCenter}>
|
||||||
<MenuItemRow
|
<MenuItemRow
|
||||||
icon={<AntDesign name="pluscircle" color="black" size={25} />}
|
icon={<AntDesign name="pluscircle" color="black" size={25} />}
|
||||||
title="Tambah Tugas"
|
title="Tambah Tugas"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
if (status == 3) return
|
||||||
setVisible(false)
|
setVisible(false)
|
||||||
router.push(`./${id}/add-task`)
|
router.push(`./${id}/add-task`)
|
||||||
}}
|
}}
|
||||||
|
disabled={status == 3}
|
||||||
/>
|
/>
|
||||||
<MenuItemRow
|
<MenuItemRow
|
||||||
icon={<MaterialCommunityIcons name="file-plus" color="black" size={25} />}
|
icon={<MaterialCommunityIcons name="file-plus" color="black" size={25} />}
|
||||||
title="Tambah File"
|
title="Tambah File"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
if (status == 3) return
|
||||||
setVisible(false)
|
setVisible(false)
|
||||||
router.push(`./${id}/add-file`)
|
router.push(`./${id}/add-file`)
|
||||||
}}
|
}}
|
||||||
|
disabled={status == 3}
|
||||||
/>
|
/>
|
||||||
|
{
|
||||||
|
( (entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision)
|
||||||
|
&&
|
||||||
|
<MenuItemRow
|
||||||
|
icon={<MaterialIcons name="groups" color="black" size={25} />}
|
||||||
|
title="Tambah Anggota"
|
||||||
|
onPress={() => {
|
||||||
|
if (status == 3) return
|
||||||
|
setVisible(false)
|
||||||
|
router.push(`./${id}/add-member`)
|
||||||
|
}}
|
||||||
|
disabled={status == 3}
|
||||||
|
/>
|
||||||
|
|
||||||
<MenuItemRow
|
}
|
||||||
icon={<MaterialIcons name="groups" color="black" size={25} />}
|
|
||||||
title="Tambah Anggota"
|
|
||||||
onPress={() => {
|
|
||||||
setVisible(false)
|
|
||||||
router.push(`./${id}/add-member`)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
<View style={[Styles.rowItemsCenter, Styles.mt15]}>
|
|
||||||
<MenuItemRow
|
{
|
||||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
( (entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision )
|
||||||
title="Edit"
|
&&
|
||||||
onPress={() => {
|
<View style={[Styles.rowItemsCenter, Styles.mt15]}>
|
||||||
setVisible(false)
|
<MenuItemRow
|
||||||
router.push(`./${id}/edit`)
|
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||||
}}
|
title="Edit"
|
||||||
/>
|
onPress={() => {
|
||||||
<MenuItemRow
|
if (status == 3) return
|
||||||
icon={<MaterialIcons name="close" color="black" size={25} />}
|
setVisible(false)
|
||||||
title="Batal"
|
router.push(`./${id}/edit`)
|
||||||
onPress={() => {
|
}}
|
||||||
setVisible(false)
|
disabled={status == 3}
|
||||||
router.push(`./${id}/cancel`)
|
/>
|
||||||
}}
|
{
|
||||||
/>
|
status == 3
|
||||||
</View>
|
?
|
||||||
|
<MenuItemRow
|
||||||
|
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||||
|
title="Hapus"
|
||||||
|
onPress={() => {
|
||||||
|
AlertKonfirmasi({
|
||||||
|
title: 'Konfirmasi',
|
||||||
|
desc: 'Apakah Anda yakin ingin menghapus tugas ini? Tugas yang dihapus tidak dapat dikembalikan',
|
||||||
|
onPress: () => { handleDelete() }
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
:
|
||||||
|
<MenuItemRow
|
||||||
|
icon={<MaterialIcons name="close" color="black" size={25} />}
|
||||||
|
title="Batal"
|
||||||
|
onPress={() => {
|
||||||
|
setVisible(false)
|
||||||
|
router.push(`./${id}/cancel`)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
}
|
||||||
</DrawerBottom>
|
</DrawerBottom>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,17 +1,47 @@
|
|||||||
import Styles from "@/constants/Styles"
|
import Styles from "@/constants/Styles"
|
||||||
|
import { apiGetDivisionOneFeature } from "@/lib/api"
|
||||||
|
import { useAuthSession } from "@/providers/AuthProvider"
|
||||||
import { AntDesign } from "@expo/vector-icons"
|
import { AntDesign } from "@expo/vector-icons"
|
||||||
import { useState } from "react"
|
import { router, useLocalSearchParams } from "expo-router"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
import { View } from "react-native"
|
import { View } from "react-native"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
import ButtonMenuHeader from "../buttonMenuHeader"
|
import ButtonMenuHeader from "../buttonMenuHeader"
|
||||||
import DrawerBottom from "../drawerBottom"
|
import DrawerBottom from "../drawerBottom"
|
||||||
import MenuItemRow from "../menuItemRow"
|
import MenuItemRow from "../menuItemRow"
|
||||||
import { router } from "expo-router"
|
|
||||||
|
|
||||||
export default function HeaderRightTaskList() {
|
export default function HeaderRightTaskList() {
|
||||||
const [isVisible, setVisible] = useState(false)
|
const [isVisible, setVisible] = useState(false)
|
||||||
|
const [isAdminDivision, setIsAdminDivision] = useState(false);
|
||||||
|
const { token, decryptToken } = useAuthSession()
|
||||||
|
const { id } = useLocalSearchParams<{ id: string }>();
|
||||||
|
const entityUser = useSelector((state: any) => state.user);
|
||||||
|
|
||||||
|
async function handleCheckAdmin() {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current));
|
||||||
|
const response = await apiGetDivisionOneFeature({
|
||||||
|
id,
|
||||||
|
user: hasil,
|
||||||
|
cat: "check-admin",
|
||||||
|
});
|
||||||
|
setIsAdminDivision(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleCheckAdmin()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
|
{
|
||||||
|
(entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision
|
||||||
|
? <ButtonMenuHeader onPress={() => { setVisible(true) }} /> : <></>
|
||||||
|
}
|
||||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
||||||
<View style={Styles.rowItemsCenter}>
|
<View style={Styles.rowItemsCenter}>
|
||||||
<MenuItemRow
|
<MenuItemRow
|
||||||
|
|||||||
14
lib/api.ts
14
lib/api.ts
@@ -550,3 +550,17 @@ export const apiAddMemberTask = async ({ data, id }: { data: { user: string, mem
|
|||||||
const response = await api.post(`/mobile/task/${id}/member`, data)
|
const response = await api.post(`/mobile/task/${id}/member`, data)
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const apiCreateTask = async (data: FormData) => {
|
||||||
|
const response = await api.post(`/mobile/task`, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const apiDeleteTask = async (data: { user: string }, id: string) => {
|
||||||
|
const response = await api.delete(`/mobile/task/${id}/lainnya`, { data })
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user