diff --git a/.gitignore b/.gitignore
index e9af40e..733c661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,9 @@ yarn-error.*
# local env files
.env*.local
+#env
+.env
+
# typescript
*.tsbuildinfo
diff --git a/app.config.js b/app.config.js
new file mode 100644
index 0000000..fae5d7b
--- /dev/null
+++ b/app.config.js
@@ -0,0 +1,72 @@
+import 'dotenv/config';
+
+export default {
+ expo: {
+ name: "mobile-darmasaba",
+ slug: "mobile-darmasaba",
+ version: "1.0.0",
+ jsEngine: "jsc",
+ orientation: "portrait",
+ icon: "./assets/images/icon.png",
+ scheme: "myapp",
+ userInterfaceStyle: "automatic",
+ newArchEnabled: false,
+ ios: {
+ supportsTablet: true,
+ bundleIdentifier: "mobiledarmasaba.app",
+ infoPlist: {
+ ITSAppUsesNonExemptEncryption: false
+ },
+ googleServicesFile: "./ios/mobiledarmasaba/GoogleService-Info.plist"
+ },
+ android: {
+ package: "mobiledarmasaba.app",
+ adaptiveIcon: {
+ foregroundImage: "./assets/images/splash-icon.png",
+ backgroundColor: "#ffffff"
+ },
+ googleServicesFile: "./google-services.json"
+ },
+ web: {
+ bundler: "metro",
+ output: "static",
+ favicon: "./assets/images/favicon.png"
+ },
+ plugins: [
+ "expo-router",
+ [
+ "expo-splash-screen",
+ {
+ image: "./assets/images/splash-icon.png",
+ imageWidth: 200,
+ resizeMode: "contain",
+ backgroundColor: "#ffffff"
+ }
+ ],
+ "expo-font",
+ "expo-image-picker",
+ "expo-web-browser",
+ [
+ "@react-native-firebase/app",
+ {
+ ios: {
+ googleServicesFile: "./ios/mobiledarmasaba/GoogleService-Info.plist"
+ }
+ }
+ ]
+ ],
+ experiments: {
+ typedRoutes: true
+ },
+ extra: {
+ router: {},
+ eas: {
+ projectId: "cfe34fb8-da8c-4004-b5c6-29d07df75cf2"
+ },
+ URL_API: process.env.URL_API,
+ URL_OTP: process.env.URL_OTP,
+ URL_STORAGE : process.env.URL_STORAGE,
+ URL_FIREBASE_DB : process.env.URL_FIREBASE_DB
+ }
+ }
+};
diff --git a/app.json b/app.json.bak
similarity index 100%
rename from app.json
rename to app.json.bak
diff --git a/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx
index 344b118..b7edf09 100644
--- a/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx
+++ b/app/(application)/division/[id]/(fitur-division)/task/[detail]/index.tsx
@@ -5,6 +5,7 @@ import HeaderRightTaskDetail from "@/components/task/headerTaskDetail";
import SectionFileTask from "@/components/task/sectionFileTask";
import SectionLinkTask from "@/components/task/sectionLinkTask";
import SectionMemberTask from "@/components/task/sectionMemberTask";
+import SectionReportTask from "@/components/task/sectionReportTask";
import SectionTanggalTugasTask from "@/components/task/sectionTanggalTugasTask";
import Styles from "@/constants/Styles";
import { apiGetTaskOne } from "@/lib/api";
@@ -89,10 +90,11 @@ export default function DetailTaskDivision() {
data?.reason != null && data?.reason != "" &&
}
-
-
-
-
+
+
+
+
+
diff --git a/app/(application)/division/[id]/(fitur-division)/task/[detail]/report.tsx b/app/(application)/division/[id]/(fitur-division)/task/[detail]/report.tsx
new file mode 100644
index 0000000..f9a5239
--- /dev/null
+++ b/app/(application)/division/[id]/(fitur-division)/task/[detail]/report.tsx
@@ -0,0 +1,129 @@
+import ButtonBackHeader from "@/components/buttonBackHeader";
+import ButtonSaveHeader from "@/components/buttonSaveHeader";
+import { InputForm } from "@/components/inputForm";
+import Styles from "@/constants/Styles";
+import { apiGetTaskOne, apiReportTask } from "@/lib/api";
+import { setUpdateTask } from "@/lib/taskUpdate";
+import { useAuthSession } from "@/providers/AuthProvider";
+import { router, Stack, useLocalSearchParams } from "expo-router";
+import { useEffect, useState } from "react";
+import { SafeAreaView, ScrollView, View } from "react-native";
+import Toast from "react-native-toast-message";
+import { useDispatch, useSelector } from "react-redux";
+
+export default function TaskDivisionReport() {
+ const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
+ const { token, decryptToken } = useAuthSession();
+ const [laporan, setLaporan] = useState("");
+ const [error, setError] = useState(false);
+ const [disable, setDisable] = useState(false);
+ const dispatch = useDispatch();
+ const update = useSelector((state: any) => state.taskUpdate);
+ const [loading, setLoading] = useState(false)
+
+ async function handleLoad() {
+ try {
+ const hasil = await decryptToken(String(token?.current));
+ const response = await apiGetTaskOne({
+ user: hasil,
+ cat: "data",
+ id: detail,
+ });
+ setLaporan(response.data.report);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ useEffect(() => {
+ handleLoad();
+ }, []);
+
+ function onValidation(val: string) {
+ setLaporan(val);
+ if (val == "" || val == "null") {
+ setError(true);
+ } else {
+ setError(false);
+ }
+ }
+
+ function checkAll() {
+ if (laporan == "" || laporan == "null" || laporan == undefined || laporan == null || error) {
+ setDisable(true);
+ } else {
+ setDisable(false);
+ }
+ }
+
+ useEffect(() => {
+ checkAll();
+ }, [laporan, error]);
+
+ async function handleUpdate() {
+ try {
+ setLoading(true)
+ const hasil = await decryptToken(String(token?.current));
+ const response = await apiReportTask(
+ {
+ report: laporan,
+ user: hasil,
+ },
+ detail
+ );
+ if (response.success) {
+ dispatch(setUpdateTask({ ...update, report: !update.report }));
+ Toast.show({ type: 'small', text1: 'Berhasil mengubah data', })
+ router.back();
+ } else {
+ Toast.show({ type: 'small', text1: response.message, })
+ }
+ } catch (error) {
+ console.error(error);
+ Toast.show({ type: 'small', text1: 'Terjadi kesalahan', })
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+ (
+ {
+ router.back();
+ }}
+ />
+ ),
+ headerTitle: "Laporan Kegiatan",
+ headerTitleAlign: "center",
+ headerRight: () => (
+ { handleUpdate() }}
+ />
+ ),
+ }}
+ />
+
+
+ { onValidation(val) }}
+ error={error}
+ errorText="Laporan kegiatan harus diisi"
+ multiline
+ />
+
+
+
+ );
+}
diff --git a/app/(application)/project/[id]/add-task.tsx b/app/(application)/project/[id]/add-task.tsx
index 640817a..78e8f3c 100644
--- a/app/(application)/project/[id]/add-task.tsx
+++ b/app/(application)/project/[id]/add-task.tsx
@@ -6,14 +6,16 @@ import Styles from "@/constants/Styles";
import { apiCreateProjectTask } from "@/lib/api";
import { setUpdateProject } from "@/lib/projectUpdate";
import { useAuthSession } from "@/providers/AuthProvider";
-import 'intl';
-import 'intl/locale-data/jsonp/id';
+import { useHeaderHeight } from '@react-navigation/elements';
import dayjs from "dayjs";
import { router, Stack, useLocalSearchParams } from "expo-router";
+import 'intl';
+import 'intl/locale-data/jsonp/id';
import { useEffect, useState } from "react";
import {
KeyboardAvoidingView,
Platform,
+ Pressable,
SafeAreaView,
ScrollView,
View
@@ -23,7 +25,6 @@ import DateTimePicker, {
DateType
} from "react-native-ui-datepicker";
import { useDispatch, useSelector } from "react-redux";
-import { useHeaderHeight } from '@react-navigation/elements';
export default function ProjectAddTask() {
const headerHeight = useHeaderHeight();
@@ -162,6 +163,10 @@ export default function ProjectAddTask() {
{
(error.endDate || error.startDate) && Tanggal tidak boleh kosong
}
+ {/* TODO */}
+
+ Detail
+
}
+
diff --git a/app/(application)/project/[id]/report.tsx b/app/(application)/project/[id]/report.tsx
new file mode 100644
index 0000000..8455332
--- /dev/null
+++ b/app/(application)/project/[id]/report.tsx
@@ -0,0 +1,128 @@
+import ButtonBackHeader from "@/components/buttonBackHeader";
+import ButtonSaveHeader from "@/components/buttonSaveHeader";
+import { InputForm } from "@/components/inputForm";
+import Styles from "@/constants/Styles";
+import { apiGetProjectOne, apiReportProject } from "@/lib/api";
+import { setUpdateProject } from "@/lib/projectUpdate";
+import { useAuthSession } from "@/providers/AuthProvider";
+import { router, Stack, useLocalSearchParams } from "expo-router";
+import { useEffect, useState } from "react";
+import { SafeAreaView, ScrollView, View } from "react-native";
+import Toast from "react-native-toast-message";
+import { useDispatch, useSelector } from "react-redux";
+
+export default function ReportProject() {
+ const { token, decryptToken } = useAuthSession();
+ const { id } = useLocalSearchParams<{ id: string }>();
+ const dispatch = useDispatch()
+ const update = useSelector((state: any) => state.projectUpdate)
+ const [laporan, setLaporan] = useState("");
+ const [error, setError] = useState(false);
+ const [disable, setDisable] = useState(false);
+ const [loading, setLoading] = useState(false)
+
+ async function handleLoad() {
+ try {
+ const hasil = await decryptToken(String(token?.current));
+ const response = await apiGetProjectOne({
+ user: hasil,
+ cat: "data",
+ id: id,
+ });
+ setLaporan(response.data.report);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ useEffect(() => {
+ handleLoad();
+ }, []);
+
+ function onValidation(val: string) {
+ setLaporan(val)
+ if (val == "" || val == "null") {
+ setError(true)
+ } else {
+ setError(false)
+ }
+ }
+
+ function checkAll() {
+ if (laporan == "" || laporan == "null" || laporan == null || laporan == undefined || error) {
+ setDisable(true)
+ } else {
+ setDisable(false)
+ }
+ }
+
+ useEffect(() => {
+ checkAll()
+ }, [laporan, error]);
+
+ async function handleUpdate() {
+ try {
+ setLoading(true)
+ const hasil = await decryptToken(String(token?.current));
+ const response = await apiReportProject({
+ report: laporan,
+ user: hasil,
+ }, id);
+ if (response.success) {
+ dispatch(setUpdateProject({ ...update, report: !update.report }))
+ Toast.show({ type: 'small', text1: 'Berhasil mengubah data', })
+ router.back();
+ } else {
+ Toast.show({ type: 'small', text1: response.message, })
+ }
+ } catch (error) {
+ console.error(error);
+ Toast.show({ type: 'small', text1: 'Terjadi kesalahan', })
+ } finally {
+ setLoading(false)
+ }
+ }
+
+
+
+ return (
+
+ (
+ {
+ router.back();
+ }}
+ />
+ ),
+ headerTitle: "Laporan Kegiatan",
+ headerTitleAlign: "center",
+ headerRight: () => (
+ { handleUpdate() }}
+ />
+ ),
+ }}
+ />
+
+
+ { onValidation(val) }}
+ error={error}
+ errorText="Judul Kegiatan harus diisi"
+ multiline
+ />
+
+
+
+ );
+}
diff --git a/app/(application)/project/create/task.tsx b/app/(application)/project/create/task.tsx
index e4723b1..f916b4d 100644
--- a/app/(application)/project/create/task.tsx
+++ b/app/(application)/project/create/task.tsx
@@ -4,6 +4,7 @@ import { InputForm } from "@/components/inputForm";
import Text from "@/components/Text";
import Styles from "@/constants/Styles";
import { setTaskCreate } from "@/lib/taskCreate";
+import { useHeaderHeight } from '@react-navigation/elements';
import dayjs from "dayjs";
import { router, Stack } from "expo-router";
import 'intl';
@@ -20,7 +21,6 @@ import DateTimePicker, {
DateType
} from "react-native-ui-datepicker";
import { useDispatch, useSelector } from "react-redux";
-import { useHeaderHeight } from '@react-navigation/elements';
export default function CreateProjectAddTask() {
const headerHeight = useHeaderHeight();
diff --git a/bun.lockb b/bun.lockb
index 3bae3c9..c827a4d 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/components/modalFloat.tsx b/components/modalFloat.tsx
index cd162e2..036d2ed 100644
--- a/components/modalFloat.tsx
+++ b/components/modalFloat.tsx
@@ -8,7 +8,7 @@ type Props = {
setVisible: (value: boolean) => void
title?: string
children: React.ReactNode
- onSubmit: () => void
+ onSubmit?: () => void
disableSubmit?: boolean
buttonHide?: boolean
}
diff --git a/components/project/headerProjectDetail.tsx b/components/project/headerProjectDetail.tsx
index 57b0c7a..d19e629 100644
--- a/components/project/headerProjectDetail.tsx
+++ b/components/project/headerProjectDetail.tsx
@@ -67,7 +67,7 @@ export default function HeaderRightProjectDetail({ id, status }: Props) {
return (
<>
{ setVisible(true) }} />
-
+
}
@@ -102,29 +102,46 @@ export default function HeaderRightProjectDetail({ id, status }: Props) {
disabled={status == 3}
/>
+
+ }
+ title="Laporan"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`/project/${id}/report`)
+ }}
+ disabled={status == 3}
+ />
+ {
+ entityUser.role != "user" && entityUser.role != "coadmin" &&
+ <>
+ }
+ title="Tambah Anggota"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`/project/${id}/add-member`)
+ }}
+ disabled={status == 3}
+ />
+ }
+ title="Edit"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`/project/${id}/edit`)
+ }}
+ disabled={status == 3}
+ />
+ >
+ }
+
{
entityUser.role != "user" && entityUser.role != "coadmin" &&
- }
- title="Tambah Anggota"
- onPress={() => {
- if (status == 3) return
- setVisible(false)
- router.push(`/project/${id}/add-member`)
- }}
- disabled={status == 3}
- />
- }
- title="Edit"
- onPress={() => {
- if (status == 3) return
- setVisible(false)
- router.push(`/project/${id}/edit`)
- }}
- disabled={status == 3}
- />
{
status == 3
?
diff --git a/components/project/modalListDetailTugasProject.tsx b/components/project/modalListDetailTugasProject.tsx
new file mode 100644
index 0000000..8c665c5
--- /dev/null
+++ b/components/project/modalListDetailTugasProject.tsx
@@ -0,0 +1,116 @@
+import Styles from "@/constants/Styles";
+import { apiGetProjectTask } from "@/lib/api";
+import { useAuthSession } from "@/providers/AuthProvider";
+import { useEffect, useState } from "react";
+import { Dimensions, View, VirtualizedList } from "react-native";
+import { InputDate } from "../inputDate";
+import ModalFloat from "../modalFloat";
+import Skeleton from "../skeleton";
+import Text from "../Text";
+
+interface Props {
+ id: string;
+ date: string;
+ timeStart: string;
+ timeEnd: string;
+}
+
+export default function ModalListDetailTugasProject({ isVisible, setVisible, idTask }: { isVisible: boolean, setVisible: (value: boolean) => void, idTask: string }) {
+ const [data, setData] = useState([])
+ const [loading, setLoading] = useState(false)
+ const { token, decryptToken } = useAuthSession()
+ const arrSkeleton = Array.from({ length: 3 }, (_, index) => index)
+ const tinggiScreen = Dimensions.get("window").height;
+ const tinggiFix = tinggiScreen * 70 / 100;
+
+
+
+ async function getData() {
+ try {
+ setLoading(true)
+ const hasil = await decryptToken(String(token?.current));
+ const res = await apiGetProjectTask({ user: hasil, id: idTask, cat: "detailTask" })
+ setData(res.data)
+ } catch (error) {
+ console.error(error)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ useEffect(() => {
+ if (isVisible) {
+ getData()
+ }
+ }, [isVisible, idTask])
+
+ const getItem = (_data: unknown, index: number): Props => ({
+ id: data[index].id,
+ date: data[index].date,
+ timeStart: data[index].timeStart,
+ timeEnd: data[index].timeEnd,
+ })
+
+ return (
+
+
+ {
+ loading ?
+ arrSkeleton.map((item: any, i: number) => {
+ return (
+
+ )
+ })
+ :
+ data.length > 0 ?
+ (
+ data.length}
+ getItem={getItem}
+ renderItem={({ item, index }: { item: Props, index: number }) => {
+ return (
+
+ {item.date}
+
+
+ { }}
+ value={item.timeStart}
+ label="Waktu Awal"
+ placeholder="--:--"
+ />
+
+
+ { }}
+ mode="time"
+ value={item.timeEnd}
+ label="Waktu Akhir"
+ placeholder="--:--"
+ disable
+ />
+
+
+
+ )
+ }}
+ keyExtractor={(item, index) => String(index)}
+ showsVerticalScrollIndicator={false}
+ />
+ )
+ :
+ Tidak ada data
+ }
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/components/project/sectionFile.tsx b/components/project/sectionFile.tsx
index 524b560..77311f4 100644
--- a/components/project/sectionFile.tsx
+++ b/components/project/sectionFile.tsx
@@ -90,12 +90,6 @@ export default function SectionFile({ status, member, refreshing }: { status: nu
}
}
- // async function download() {
- // const destination = new Directory(Paths.document, 'pdfs');
- // const filename = "dummy.pdf";
- // const result = await File.downloadFileAsync('https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', destination);
- // }
-
const openFile = () => {
setModal(false)
diff --git a/components/project/sectionReportProject.tsx b/components/project/sectionReportProject.tsx
new file mode 100644
index 0000000..18e7b82
--- /dev/null
+++ b/components/project/sectionReportProject.tsx
@@ -0,0 +1,55 @@
+import Styles from "@/constants/Styles";
+import { apiGetProjectOne } from "@/lib/api";
+import { useAuthSession } from "@/providers/AuthProvider";
+import { useLocalSearchParams } from "expo-router";
+import { useEffect, useState } from "react";
+import { View } from "react-native";
+import { useSelector } from "react-redux";
+import Text from "../Text";
+import TextExpandable from "../textExpandable";
+
+export default function SectionReportProject({ refreshing }: { refreshing?: boolean }) {
+ const update = useSelector((state: any) => state.projectUpdate)
+ const { token, decryptToken } = useAuthSession();
+ const { id } = useLocalSearchParams<{ id: string }>();
+ const [data, setData] = useState("");
+
+ async function handleLoad() {
+ try {
+ const hasil = await decryptToken(String(token?.current));
+ const response = await apiGetProjectOne({
+ user: hasil,
+ cat: "data",
+ id: id,
+ });
+ setData(response.data.report);
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ useEffect(() => {
+ handleLoad();
+ }, [update.report]);
+
+ useEffect(() => {
+ if (refreshing)
+ handleLoad();
+ }, [refreshing]);
+
+ return (
+ <>
+ {
+ data != "" && data != null &&
+
+
+ Laporan Kegiatan
+
+
+
+
+
+ }
+ >
+ );
+}
diff --git a/components/project/sectionTanggalTugas.tsx b/components/project/sectionTanggalTugas.tsx
index ee3cb40..285b55e 100644
--- a/components/project/sectionTanggalTugas.tsx
+++ b/components/project/sectionTanggalTugas.tsx
@@ -15,6 +15,7 @@ import MenuItemRow from "../menuItemRow";
import ModalSelect from "../modalSelect";
import SkeletonTask from "../skeletonTask";
import Text from "../Text";
+import ModalListDetailTugasProject from "./modalListDetailTugasProject";
type Props = {
id: string;
@@ -33,6 +34,7 @@ export default function SectionTanggalTugasProject({ status, member, refreshing
const [isModal, setModal] = useState(false);
const [isSelect, setSelect] = useState(false);
const { token, decryptToken } = useAuthSession();
+ const [modalDetail, setModalDetail] = useState(false)
const { id } = useLocalSearchParams<{ id: string }>();
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true)
@@ -188,6 +190,24 @@ export default function SectionTanggalTugasProject({ status, member, refreshing
}}
/>
+
+ }
+ title="Detail Waktu"
+ onPress={() => {
+ setModal(false);
+ setTimeout(() => {
+ setModalDetail(true)
+ }, 600)
+ }}
+ />
+
+
}
title="Hapus Tugas"
@@ -213,6 +233,12 @@ export default function SectionTanggalTugasProject({ status, member, refreshing
open={isSelect}
valChoose={String(tugas.status)}
/>
+
+
>
);
}
diff --git a/components/task/headerTaskDetail.tsx b/components/task/headerTaskDetail.tsx
index 816738f..392c6da 100644
--- a/components/task/headerTaskDetail.tsx
+++ b/components/task/headerTaskDetail.tsx
@@ -101,7 +101,7 @@ export default function HeaderRightTaskDetail({ id, division, status }: Props) {
:
{ setVisible(true) }} />
}
-
+
}
@@ -137,31 +137,49 @@ export default function HeaderRightTaskDetail({ id, division, status }: Props) {
disabled={status == 3}
/>
+
+ }
+ title="Laporan"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`./${id}/report`)
+ }}
+ disabled={status == 3}
+ />
+ {
+ ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision)
+ &&
+ <>
+ }
+ title="Tambah Anggota"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`./${id}/add-member`)
+ }}
+ disabled={status == 3}
+ />
+ }
+ title="Edit"
+ onPress={() => {
+ if (status == 3) return
+ setVisible(false)
+ router.push(`./${id}/edit`)
+ }}
+ disabled={status == 3}
+ />
+ >
+ }
+
{
((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision)
&&
- }
- title="Tambah Anggota"
- onPress={() => {
- if (status == 3) return
- setVisible(false)
- router.push(`./${id}/add-member`)
- }}
- disabled={status == 3}
- />
- }
- title="Edit"
- onPress={() => {
- if (status == 3) return
- setVisible(false)
- router.push(`./${id}/edit`)
- }}
- disabled={status == 3}
- />
{
status == 3
?
@@ -190,7 +208,7 @@ export default function HeaderRightTaskDetail({ id, division, status }: Props) {
}
}
-
+
state.taskUpdate)
+ const { token, decryptToken } = useAuthSession()
+ const { id, detail } = useLocalSearchParams<{ id: string, detail: string }>();
+ const [data, setData] = useState('')
+
+ async function handleLoad() {
+ try {
+ const hasil = await decryptToken(String(token?.current))
+ const response = await apiGetTaskOne({ id: detail, user: hasil, cat: 'data' })
+ setData(response.data.report)
+ } catch (error) {
+ console.error(error)
+ }
+ }
+
+ useEffect(() => {
+ handleLoad()
+ }, [update.report])
+
+ useEffect(() => {
+ if (refreshing)
+ handleLoad();
+ }, [refreshing]);
+
+
+ return (
+ <>
+ {
+ data != "" && data != null &&
+
+
+ Laporan Kegiatan
+
+
+
+
+
+ }
+ >
+ )
+}
\ No newline at end of file
diff --git a/components/textExpandable.tsx b/components/textExpandable.tsx
new file mode 100644
index 0000000..13e4413
--- /dev/null
+++ b/components/textExpandable.tsx
@@ -0,0 +1,83 @@
+import Styles from "@/constants/Styles";
+import { useRef, useState, useEffect } from "react";
+import { Animated, Pressable, View } from "react-native";
+import Text from "./Text";
+
+export default function TextExpandable({ content, maxLines }: { content: string, maxLines: number }) {
+ const [isExpanded, setIsExpanded] = useState(false);
+ const [shouldShowMore, setShouldShowMore] = useState(false);
+ const [collapsedHeight, setCollapsedHeight] = useState(0);
+ const [fullHeight, setFullHeight] = useState(0);
+ const animatedHeight = useRef(new Animated.Value(0)).current;
+
+ const measureCollapsed = (e: any) => {
+ if (collapsedHeight === 0) {
+ setCollapsedHeight(e.nativeEvent.layout.height);
+ animatedHeight.setValue(e.nativeEvent.layout.height);
+ }
+ };
+
+ const measureFull = (e: any) => {
+ if (fullHeight === 0) {
+ setFullHeight(e.nativeEvent.layout.height);
+ }
+ };
+
+ // Cek apakah memang perlu "View More"
+ useEffect(() => {
+ if (collapsedHeight > 0 && fullHeight > 0) {
+ setShouldShowMore(fullHeight > collapsedHeight + 1); // +1 untuk toleransi float
+ }
+ }, [collapsedHeight, fullHeight]);
+
+ const toggleExpand = () => {
+ Animated.timing(animatedHeight, {
+ toValue: isExpanded ? collapsedHeight : fullHeight,
+ duration: 300,
+ useNativeDriver: false,
+ }).start();
+ setIsExpanded(!isExpanded);
+ };
+
+ return (
+
+ {/* Hidden full text for measurement */}
+
+
+ {content}
+
+
+
+ {/* Collapsed text for measurement */}
+
+
+ {content}
+
+
+
+ {/* Animated visible text */}
+
+
+ {content}
+
+
+
+ {shouldShowMore && (
+
+
+ {isExpanded ? 'View Less' : 'View More'}
+
+
+ )}
+
+ );
+};
diff --git a/constants/Colors.ts b/constants/Colors.ts
index 54c7695..be9eec1 100644
--- a/constants/Colors.ts
+++ b/constants/Colors.ts
@@ -1,8 +1,3 @@
-/**
- * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
- * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
- */
-
const tintColorLight = '#19345E';
const tintColorDark = '#fff';
diff --git a/constants/ConstEnv.ts b/constants/ConstEnv.ts
new file mode 100644
index 0000000..65de1b0
--- /dev/null
+++ b/constants/ConstEnv.ts
@@ -0,0 +1,5 @@
+import Constants from 'expo-constants';
+
+export const ConstEnv = {
+ url_storage : Constants?.expoConfig?.extra?.URL_STORAGE
+}
\ No newline at end of file
diff --git a/constants/Styles.ts b/constants/Styles.ts
index 4af2ba9..88b95a3 100644
--- a/constants/Styles.ts
+++ b/constants/Styles.ts
@@ -45,8 +45,7 @@ const Styles = StyleSheet.create({
fontWeight: 'bold',
},
textLink: {
- lineHeight: 30,
- fontSize: 16,
+ fontSize: 14,
color: '#0a7ea4',
},
textInformation: {
@@ -270,6 +269,12 @@ const Styles = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'center'
},
+ btnLainnya: {
+ alignSelf: 'flex-start',
+ backgroundColor: '#19345E',
+ paddingVertical: 5,
+ marginVertical: 5
+ },
btnMenuRow: {
width: '33%',
alignItems: 'center'
@@ -593,6 +598,11 @@ const Styles = StyleSheet.create({
bottom: 5,
right: 5,
position: 'absolute'
+ },
+ hidden: {
+ position: 'absolute',
+ opacity: 0,
+ zIndex: -1,
}
})
diff --git a/ios/mobiledarmasaba.xcodeproj/project.pbxproj b/ios/mobiledarmasaba.xcodeproj/project.pbxproj
index 9ce7135..0d873ad 100644
--- a/ios/mobiledarmasaba.xcodeproj/project.pbxproj
+++ b/ios/mobiledarmasaba.xcodeproj/project.pbxproj
@@ -252,8 +252,6 @@
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
);
name = "[CP-User] [RNFB] Core Configuration";
- outputPaths = (
- );
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#!/usr/bin/env bash\n#\n# Copyright (c) 2016-present Invertase Limited & Contributors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this library except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##########################################################################\n##########################################################################\n#\n# NOTE THAT IF YOU CHANGE THIS FILE YOU MUST RUN pod install AFTERWARDS\n#\n# This file is installed as an Xcode build script in the project file\n# by cocoapods, and you will not see your changes until you pod install\n#\n##########################################################################\n##########################################################################\n\nset -e\n\n_MAX_LOOKUPS=2;\n_SEARCH_RESULT=''\n_RN_ROOT_EXISTS=''\n_CURRENT_LOOKUPS=1\n_JSON_ROOT=\"'react-native'\"\n_JSON_FILE_NAME='firebase.json'\n_JSON_OUTPUT_BASE64='e30=' # { }\n_CURRENT_SEARCH_DIR=${PROJECT_DIR}\n_PLIST_BUDDY=/usr/libexec/PlistBuddy\n_TARGET_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n_DSYM_PLIST=\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Info.plist\"\n\n# plist arrays\n_PLIST_ENTRY_KEYS=()\n_PLIST_ENTRY_TYPES=()\n_PLIST_ENTRY_VALUES=()\n\nfunction setPlistValue {\n echo \"info: setting plist entry '$1' of type '$2' in file '$4'\"\n ${_PLIST_BUDDY} -c \"Add :$1 $2 '$3'\" $4 || echo \"info: '$1' already exists\"\n}\n\nfunction getFirebaseJsonKeyValue () {\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$1'); puts output[$_JSON_ROOT]['$2']\"\n else\n echo \"\"\n fi;\n}\n\nfunction jsonBoolToYesNo () {\n if [[ $1 == \"false\" ]]; then\n echo \"NO\"\n elif [[ $1 == \"true\" ]]; then\n echo \"YES\"\n else echo \"NO\"\n fi\n}\n\necho \"info: -> RNFB build script started\"\necho \"info: 1) Locating ${_JSON_FILE_NAME} file:\"\n\nif [[ -z ${_CURRENT_SEARCH_DIR} ]]; then\n _CURRENT_SEARCH_DIR=$(pwd)\nfi;\n\nwhile true; do\n _CURRENT_SEARCH_DIR=$(dirname \"$_CURRENT_SEARCH_DIR\")\n if [[ \"$_CURRENT_SEARCH_DIR\" == \"/\" ]] || [[ ${_CURRENT_LOOKUPS} -gt ${_MAX_LOOKUPS} ]]; then break; fi;\n echo \"info: ($_CURRENT_LOOKUPS of $_MAX_LOOKUPS) Searching in '$_CURRENT_SEARCH_DIR' for a ${_JSON_FILE_NAME} file.\"\n _SEARCH_RESULT=$(find \"$_CURRENT_SEARCH_DIR\" -maxdepth 2 -name ${_JSON_FILE_NAME} -print | /usr/bin/head -n 1)\n if [[ ${_SEARCH_RESULT} ]]; then\n echo \"info: ${_JSON_FILE_NAME} found at $_SEARCH_RESULT\"\n break;\n fi;\n _CURRENT_LOOKUPS=$((_CURRENT_LOOKUPS+1))\ndone\n\nif [[ ${_SEARCH_RESULT} ]]; then\n _JSON_OUTPUT_RAW=$(cat \"${_SEARCH_RESULT}\")\n _RN_ROOT_EXISTS=$(ruby -Ku -e \"require 'rubygems';require 'json'; output=JSON.parse('$_JSON_OUTPUT_RAW'); puts output[$_JSON_ROOT]\" || echo '')\n\n if [[ ${_RN_ROOT_EXISTS} ]]; then\n if ! python3 --version >/dev/null 2>&1; then echo \"python3 not found, firebase.json file processing error.\" && exit 1; fi\n _JSON_OUTPUT_BASE64=$(python3 -c 'import json,sys,base64;print(base64.b64encode(bytes(json.dumps(json.loads(open('\"'${_SEARCH_RESULT}'\"', '\"'rb'\"').read())['${_JSON_ROOT}']), '\"'utf-8'\"')).decode())' || echo \"e30=\")\n fi\n\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n\n # config.app_data_collection_default_enabled\n _APP_DATA_COLLECTION_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_data_collection_default_enabled\")\n if [[ $_APP_DATA_COLLECTION_ENABLED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseDataCollectionDefaultEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_DATA_COLLECTION_ENABLED\")\")\n fi\n\n # config.analytics_auto_collection_enabled\n _ANALYTICS_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_auto_collection_enabled\")\n if [[ $_ANALYTICS_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_COLLECTION\")\")\n fi\n\n # config.analytics_collection_deactivated\n _ANALYTICS_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_collection_deactivated\")\n if [[ $_ANALYTICS_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_DEACTIVATED\")\")\n fi\n\n # config.analytics_idfv_collection_enabled\n _ANALYTICS_IDFV_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_idfv_collection_enabled\")\n if [[ $_ANALYTICS_IDFV_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_IDFV_COLLECTION\")\")\n fi\n\n # config.analytics_default_allow_analytics_storage\n _ANALYTICS_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_analytics_storage\")\n if [[ $_ANALYTICS_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_ANALYTICS_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_storage\n _ANALYTICS_AD_STORAGE=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_storage\")\n if [[ $_ANALYTICS_AD_STORAGE ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_STORAGE\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_STORAGE\")\")\n fi\n\n # config.analytics_default_allow_ad_user_data\n _ANALYTICS_AD_USER_DATA=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_user_data\")\n if [[ $_ANALYTICS_AD_USER_DATA ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_USER_DATA\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AD_USER_DATA\")\")\n fi\n\n # config.analytics_default_allow_ad_personalization_signals\n _ANALYTICS_PERSONALIZATION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"analytics_default_allow_ad_personalization_signals\")\n if [[ $_ANALYTICS_PERSONALIZATION ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_PERSONALIZATION\")\")\n fi\n\n # config.analytics_registration_with_ad_network_enabled\n _ANALYTICS_REGISTRATION_WITH_AD_NETWORK=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_registration_with_ad_network_enabled\")\n if [[ $_ANALYTICS_REGISTRATION_WITH_AD_NETWORK ]]; then\n _PLIST_ENTRY_KEYS+=(\"GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_REGISTRATION_WITH_AD_NETWORK\")\")\n fi\n\n # config.google_analytics_automatic_screen_reporting_enabled\n _ANALYTICS_AUTO_SCREEN_REPORTING=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"google_analytics_automatic_screen_reporting_enabled\")\n if [[ $_ANALYTICS_AUTO_SCREEN_REPORTING ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAutomaticScreenReportingEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_ANALYTICS_AUTO_SCREEN_REPORTING\")\")\n fi\n\n # config.perf_auto_collection_enabled\n _PERF_AUTO_COLLECTION=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_auto_collection_enabled\")\n if [[ $_PERF_AUTO_COLLECTION ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_enabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_AUTO_COLLECTION\")\")\n fi\n\n # config.perf_collection_deactivated\n _PERF_DEACTIVATED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"perf_collection_deactivated\")\n if [[ $_PERF_DEACTIVATED ]]; then\n _PLIST_ENTRY_KEYS+=(\"firebase_performance_collection_deactivated\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_PERF_DEACTIVATED\")\")\n fi\n\n # config.messaging_auto_init_enabled\n _MESSAGING_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"messaging_auto_init_enabled\")\n if [[ $_MESSAGING_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseMessagingAutoInitEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_MESSAGING_AUTO_INIT\")\")\n fi\n\n # config.in_app_messaging_auto_colllection_enabled\n _FIAM_AUTO_INIT=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"in_app_messaging_auto_collection_enabled\")\n if [[ $_FIAM_AUTO_INIT ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseInAppMessagingAutomaticDataCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_FIAM_AUTO_INIT\")\")\n fi\n\n # config.app_check_token_auto_refresh\n _APP_CHECK_TOKEN_AUTO_REFRESH=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"app_check_token_auto_refresh\")\n if [[ $_APP_CHECK_TOKEN_AUTO_REFRESH ]]; then\n _PLIST_ENTRY_KEYS+=(\"FirebaseAppCheckTokenAutoRefreshEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"$(jsonBoolToYesNo \"$_APP_CHECK_TOKEN_AUTO_REFRESH\")\")\n fi\n\n # config.crashlytics_disable_auto_disabler - undocumented for now - mainly for debugging, document if becomes useful\n _CRASHLYTICS_AUTO_DISABLE_ENABLED=$(getFirebaseJsonKeyValue \"$_JSON_OUTPUT_RAW\" \"crashlytics_disable_auto_disabler\")\n if [[ $_CRASHLYTICS_AUTO_DISABLE_ENABLED == \"true\" ]]; then\n echo \"Disabled Crashlytics auto disabler.\" # do nothing\n else\n _PLIST_ENTRY_KEYS+=(\"FirebaseCrashlyticsCollectionEnabled\")\n _PLIST_ENTRY_TYPES+=(\"bool\")\n _PLIST_ENTRY_VALUES+=(\"NO\")\n fi\nelse\n _PLIST_ENTRY_KEYS+=(\"firebase_json_raw\")\n _PLIST_ENTRY_TYPES+=(\"string\")\n _PLIST_ENTRY_VALUES+=(\"$_JSON_OUTPUT_BASE64\")\n echo \"warning: A firebase.json file was not found, whilst this file is optional it is recommended to include it to configure firebase services in React Native Firebase.\"\nfi;\n\necho \"info: 2) Injecting Info.plist entries: \"\n\n# Log out the keys we're adding\nfor i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n echo \" -> $i) ${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\"\ndone\n\nfor plist in \"${_TARGET_PLIST}\" \"${_DSYM_PLIST}\" ; do\n if [[ -f \"${plist}\" ]]; then\n\n # paths with spaces break the call to setPlistValue. temporarily modify\n # the shell internal field separator variable (IFS), which normally\n # includes spaces, to consist only of line breaks\n oldifs=$IFS\n IFS=\"\n\"\n\n for i in \"${!_PLIST_ENTRY_KEYS[@]}\"; do\n setPlistValue \"${_PLIST_ENTRY_KEYS[$i]}\" \"${_PLIST_ENTRY_TYPES[$i]}\" \"${_PLIST_ENTRY_VALUES[$i]}\" \"${plist}\"\n done\n\n # restore the original internal field separator value\n IFS=$oldifs\n else\n echo \"warning: A Info.plist build output file was not found (${plist})\"\n fi\ndone\n\necho \"info: <- RNFB build script finished\"\n";
@@ -489,7 +487,10 @@
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
- OTHER_LDFLAGS = "$(inherited) ";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ " ",
+ );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -544,7 +545,10 @@
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
- OTHER_LDFLAGS = "$(inherited) ";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ " ",
+ );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = false;
diff --git a/lib/api.ts b/lib/api.ts
index aa71619..3a485df 100644
--- a/lib/api.ts
+++ b/lib/api.ts
@@ -1,9 +1,11 @@
import axios from 'axios';
+import Constants from 'expo-constants';
const api = axios.create({
// baseURL: 'http://10.0.2.2:3000/api',
// baseURL: 'https://stg-darmasaba.wibudev.com/api',
- baseURL: 'http://192.168.1.110:3000/api',
+ // baseURL: 'http://192.168.154.198:3000/api',
+ baseURL: Constants?.expoConfig?.extra?.URL_API
});
export const apiCheckPhoneLogin = async (body: { phone: string }) => {
@@ -12,7 +14,7 @@ export const apiCheckPhoneLogin = async (body: { phone: string }) => {
}
export const apiSendOtp = async (body: { phone: string, otp: number }) => {
- const res = await axios.get(`https://wa.wibudev.com/code?nom=${body.phone}&text=*DARMASABA*%0A%0A
+ const res = await axios.get(`${Constants.expoConfig?.extra?.URL_OTP}/code?nom=${body.phone}&text=*DARMASABA*%0A%0A
JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(body.otp)}*`)
return res.status
}
@@ -274,6 +276,11 @@ export const apiEditProject = async (data: { name: string, user: string }, id: s
return response.data;
};
+export const apiReportProject = async (data: { report: string, user: string }, id: string) => {
+ const response = await api.put(`/mobile/project/${id}/lainnya`, data)
+ return response.data;
+};
+
export const apiCreateProjectTask = async ({ data, id }: { data: { name: string, dateStart: string, user: string, dateEnd: string }, id: string }) => {
const response = await api.post(`/mobile/project/${id}`, data)
return response.data;
@@ -294,8 +301,8 @@ export const apiDeleteProjectTask = async (data: { user: string, idProject: stri
return response.data
};
-export const apiGetProjectTask = async ({ user, id }: { user: string, id: string }) => {
- const response = await api.get(`mobile/project/detail/${id}?user=${user}`);
+export const apiGetProjectTask = async ({ user, id, cat }: { user: string, id: string, cat?: string }) => {
+ const response = await api.get(`mobile/project/detail/${id}?user=${user}${cat ? `&cat=${cat}` : ""}`);
return response.data;
};
@@ -584,6 +591,11 @@ export const apiEditTask = async (data: { title: string, user: string }, id: str
return response.data;
};
+export const apiReportTask = async (data: { report: string, user: string }, id: string) => {
+ const response = await api.put(`/mobile/task/${id}/lainnya`, data)
+ return response.data;
+};
+
export const apiCancelTask = async (data: { user: string, reason: string }, id: string) => {
const response = await api.delete(`mobile/task/${id}`, { data })
return response.data
diff --git a/lib/projectUpdate.ts b/lib/projectUpdate.ts
index 8174441..3453957 100644
--- a/lib/projectUpdate.ts
+++ b/lib/projectUpdate.ts
@@ -9,6 +9,7 @@ const projectUpdate = createSlice({
file: false,
member: false,
link: false,
+ report: false,
},
reducers: {
setUpdateProject: (state, action) => {
diff --git a/lib/taskUpdate.ts b/lib/taskUpdate.ts
index 2e7fbf0..8f68e53 100644
--- a/lib/taskUpdate.ts
+++ b/lib/taskUpdate.ts
@@ -9,6 +9,7 @@ const taskUpdate = createSlice({
file: false,
member: false,
link: false,
+ report: false,
},
reducers: {
setUpdateTask: (state, action) => {
diff --git a/package.json b/package.json
index 61d2a97..815c340 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"crypto-es": "^2.1.0",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.11.13",
+ "dotenv": "^17.2.1",
"expo": "^53.0.9",
"expo-blur": "~14.1.4",
"expo-clipboard": "^7.1.4",