diff --git a/src/screens/Home.js b/src/screens/Home.js
index d36883a..d7b1dff 100644
--- a/src/screens/Home.js
+++ b/src/screens/Home.js
@@ -1,32 +1,56 @@
-import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
-import { Card, Text, Avatar, useTheme, IconButton, FAB, Button, Tooltip, TouchableRipple, Badge } from 'react-native-paper';
-import { RefreshControl, StyleSheet, View, Alert, PermissionsAndroid, Platform, ToastAndroid, ScrollView, StatusBar } from 'react-native';
+import React, { useEffect, useCallback, useState, useRef } from 'react';
+import {
+ View,
+ StyleSheet,
+ ScrollView,
+ StatusBar,
+ PermissionsAndroid,
+ RefreshControl
+} from 'react-native';
+import { Card, Avatar, IconButton, Button, Text, TouchableRipple, ActivityIndicator } from 'react-native-paper';
import { launchCamera } from 'react-native-image-picker';
import { useFocusEffect } from '@react-navigation/native';
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker';
-import { useDispatch, useSelector } from 'react-redux';
-import { setShipmentData } from '../appredux/actions';
-import { store } from '../appredux/store';
+import { useSelector } from 'react-redux';
import RNFS from 'react-native-fs';
-import { getCoords } from '../utils/geolocation';
-import { strings } from '../utils/i18n';
import moment from 'moment';
import 'moment/locale/id';
-moment.locale('id');
-import person from '../assets/images/courier_man.png'
+import uuid from 'react-native-uuid';
import {
BottomSheetModal,
BottomSheetModalProvider,
- BottomSheetView
+ BottomSheetView,
} from '@gorhom/bottom-sheet';
import Icon from 'react-native-vector-icons/AntDesign';
-import { requestAccessStoragePermission } from '../utils/storage';
+import Toast from 'react-native-toast-message';
+
+import person from '../assets/images/courier_man.png';
import { colors } from '../utils/color';
+import { getCoords } from '../utils/geolocation';
+import { strings } from '../utils/i18n';
+import { PATH_PRESENCE } from '../config/imageFolder';
+import { storeData, resendFailedAttachments } from '../services/sqlite/attachment';
+import RequestModule from '../services/api/request';
+import { requestAccessStoragePermission } from '../utils/storage';
+// import renderSkeleton from '../components/renderSkeleton';
+moment.locale('id');
const HomeScreen = ({ route, navigation }) => {
- const theme = useTheme();
+ const request = new RequestModule('presence')
+ const { user } = useSelector(state => state.userReducer)
const [currentTime, setCurrentTime] = useState(moment(new Date()));
+ const [existingAttachmentNumber, setExistingAttachmentNumber] = useState('');
+ const [imageData, setImageData] = useState([]);
+ const [idData, setIdData] = useState(null);
+ const [dataPresence, setDataPresence] = useState([])
+ const [lastPresence, setLastPresence] = useState(null)
const bottomSheetModal = useRef(null);
+ const [refreshing, setRefreshing] = useState(false);
+ const [loading, setLoading] = useState(false);
+ const [loadingSkeleton, setLoadingSkeleton] = useState(false);
+ const presenceText = lastPresence && lastPresence.length > 0 ? 'presenceOut' : 'presenceIn';
+ const messageText = lastPresence && lastPresence.length > 0 ? 'presenceOutMessage' : 'presenceInMessage';
+
useFocusEffect(
useCallback(() => {
let homeTimer = setInterval(() => {
@@ -35,185 +59,333 @@ const HomeScreen = ({ route, navigation }) => {
return () => {
clearInterval(homeTimer);
}
+
}, [])
);
+ const onRefresh = React.useCallback(() => {
+ setRefreshing(true);
+ setLoadingSkeleton(true);
+ getPresenceHistory();
+ }, []);
+
+ useEffect(() => {
+ if (existingAttachmentNumber === '') {
+ setExistingAttachmentNumber(uuid.v4())
+ }
+ getPresenceHistory()
+ getLastPresence()
+ resendFailedAttachments();
+ }, [])
- const dummyData = [
- { id: '1', date: '2024-02-09', timeIn: '08:00', timeOut: '17:00', shift: "Pagi", duration: new Date(0, 0, 0, 8, 0, 0) },
- { id: '2', date: '2024-02-10', timeIn: '08:15', timeOut: '17:30', shift: "Pagi", duration: new Date(0, 0, 0, 8, 30, 0) },
- { id: '3', date: '2024-02-11', timeIn: '08:10', timeOut: '17:20', shift: "Pagi", duration: new Date(0, 0, 0, 9, 0, 0) },
- { id: '4', date: '2024-02-12', timeIn: '08:10', timeOut: '17:20', shift: "Pagi", duration: new Date(0, 0, 0, 10, 0, 0) },
- { id: '5', date: '2024-02-13', timeIn: '08:10', timeOut: '17:20', shift: "Pagi", duration: new Date(0, 0, 0, 11, 0, 0) },
- { id: '6', date: '2024-02-14', timeIn: '08:10', timeOut: '17:20', shift: "Pagi", duration: new Date(0, 0, 0, 9, 30, 0) },
- { id: '7', date: '2024-02-15', timeIn: '08:10', timeOut: '17:20', shift: "Pagi", duration: new Date(0, 0, 0, 12, 0, 0) },
- ];
+
+ const getLastPresence = async () => {
+ const payload = {
+ paging: { start: 0, length: 1 },
+ columns: [
+ { name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND' },
+ { name: 'date_presence', logic_operator: '=', operator: 'AND', value: moment().format('YYYY-MM-DD') },
+ { name: 'hr_to_project_id', logic_operator: '=', operator: 'AND', value: "1" }
+ ],
+ orders: { columns: ['created_at', 'id'], ascending: false }
+ };
+ const result = await request.getDataSearch(payload);
+ if (result && result.status === 200) {
+ setLastPresence(result.data.data)
+ setIdData(result.data.data[0]?.id)
+ }
+ }
+ const getPresenceHistory = async () => {
+ if (loadingSkeleton) return;
+ setLoadingSkeleton(true);
+ const payload = {
+ paging: { start: 0, length: 7 },
+ columns: [
+ { name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND' },
+ { name: 'hr_to_project_id', logic_operator: '=', operator: 'AND', value: "1" }
+ ],
+ orders: { columns: ['created_at', 'id'], ascending: false }
+ };
+ const result = await request.getDataSearch(payload);
+ if (result && result.status === 200) {
+ setDataPresence(result.data.data)
+ }
+ setRefreshing(false);
+ setLoadingSkeleton(false);
+ }
const handlePresensiPress = useCallback(() => {
bottomSheetModal.current?.present();
}, []);
- const handlePresensiConfirm = async () => {
+ const handleSendPresenceData = async () => {
try {
- const granted = await PermissionsAndroid.request(
- PermissionsAndroid.PERMISSIONS.CAMERA,
- {
- title: strings('takePicture.cameraPermissionTitle'),
- message: strings('takePicture.cameraPermissionMessage'),
- buttonNeutral: strings('takePicture.cameraPermissionBtnNeutral'),
- buttonNegative: strings('takePicture.cameraPermissionBtnCancel'),
- buttonPositive: strings('takePicture.cameraPermissionBtnPositive'),
- },
- );
- if (granted === PermissionsAndroid.RESULTS.GRANTED) {
- handleLaunchCamera()
- } else {
- }
- } catch (err) {
- console.warn(err);
+ const currentDateTime = moment(new Date());
+ getCoords(async ({ lat, lon, timestamp }) => {
+ if (lat === null || lon === null) {
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorFetchingLocation'),
+ });
+ return;
+ }
+
+ const payload = {
+ assign_hr_id: 1,
+ datePresence: currentDateTime,
+ in_time: currentDateTime,
+ in_lat: lat,
+ in_lon: lon,
+ shift: 'Pagi',
+ status: 'Present',
+ attachment_number: existingAttachmentNumber,
+ send_time: currentDateTime
+ };
+
+ try {
+ const result = await request.addData(payload);
+ if (result.status === 201) {
+ Toast.show({
+ type: 'success',
+ text1: strings('presence.dataSentSuccessfully'),
+ });
+ getLastPresence()
+ getPresenceHistory()
+ setLoading(false)
+ } else {
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.failedSendDataPresence'),
+ });
+ setLoading(false)
+ }
+ setLoading(false)
+ } catch (error) {
+ console.error("Network error sending presence data:", error);
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorMessage'),
+ });
+ setLoading(false)
+ }
+ });
+ } catch (error) {
+ console.error("Error sending presence data:", error);
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorMessage'),
+ });
+ setLoading(false)
}
- bottomSheetModal.current?.dismiss();
};
+ const handlePresensiConfirm = async () => {
+ if (lastPresence && lastPresence.length > 0) {
+ try {
+ const currentDateTime = moment(new Date());
+ getCoords(async ({ lat, lon, timestamp }) => {
+ if (lat === null || lon === null) {
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorFetchingLocation'),
+ });
+ return;
+ }
+
+ const inTime = moment(lastPresence[0].in_time);
+ const outTime = moment(currentDateTime);
+ const duration = outTime.diff(inTime, 'minutes');
+
+ const payloadEdit = {
+ assign_hr_id: 1,
+ out_time: currentDateTime,
+ duration: duration,
+ out_lat: lat,
+ out_lon: lon,
+ };
+
+ try {
+ const result = await request.updateData(idData, payloadEdit);
+ console.log("result", result);
+ if (result.status === 200) {
+ Toast.show({
+ type: 'success',
+ text1: strings('presence.dataSentOutSuccessfully'),
+ });
+ getLastPresence()
+ getPresenceHistory()
+ setLoading(false)
+ bottomSheetModal.current?.dismiss();
+ } else {
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.failedSendDataPresence'),
+ });
+ setLoading(false)
+ }
+ setLoading(false)
+ } catch (error) {
+ console.error("Network error sending presence data:", error);
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorMessage'),
+ });
+ setLoading(false)
+ }
+ });
+ } catch (error) {
+ console.error("Error sending presence data:", error);
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorMessage'),
+ });
+ setLoading(false)
+ }
+ } else {
+ try {
+ const granted = await PermissionsAndroid.request(
+ PermissionsAndroid.PERMISSIONS.CAMERA,
+ {
+ title: strings('takePicture.cameraPermissionTitle'),
+ message: strings('takePicture.cameraPermissionMessage'),
+ buttonNeutral: strings('takePicture.cameraPermissionBtnNeutral'),
+ buttonNegative: strings('takePicture.cameraPermissionBtnCancel'),
+ buttonPositive: strings('takePicture.cameraPermissionBtnPositive'),
+ },
+ );
+ if (granted === PermissionsAndroid.RESULTS.GRANTED) {
+ handleLaunchCamera();
+ } else {
+ console.warn('Camera permission denied');
+ }
+ } catch (err) {
+ console.warn(err);
+ }
+ bottomSheetModal.current?.dismiss();
+ }
+ }
+
+ const handleUploadImage = async (dataImage, imageCategory) => {
+ const formData = new FormData();
+ for (const { attachment_number, imageFile, type, name, data, description } of dataImage) {
+ formData.append('attachment_number', attachment_number);
+ formData.append('description', description);
+ formData.append('files', {
+ uri: imageFile,
+ type: type,
+ name: name
+ });
+
+ }
+ const result = await request.uploadImage(imageCategory, formData);
+ }
+
const handleLaunchCamera = () => {
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
- cameraType: 'front',
+ cameraType: 'back',
maxWidth: 768,
maxHeight: 1024,
saveToPhotos: false,
includeBase64: true
};
- launchCamera(options, (response) => {
+ launchCamera(options, async (response) => {
if (response.didCancel) {
- // console.log('User cancelled image picker');
+ console.log('User cancelled image picker');
} else if (response.error) {
- // console.log('ImagePicker Error: ', response.error);
- } else if (response.customButton) {
- // console.log('User tapped custom button: ', response.customButton);
- // alert(response.customButton);
+ console.log('ImagePicker Error: ', response.error);
} else {
- // console.log('response', response);
- // response:
- // {
- // "assets": [
- // {
- // "base64": "/9j/4QBqRXhpZgAATU0AKgAAAAgABAEAAAQAAAABAAAEAAEBAAQ......"
- // "fileName": "rn_image_picker_lib_temp_f5afd4ec-a31b-4854-8dc7-5c5dd750e3c6.jpg",
- // "fileSize": 595470,
- // "height": 1024,
- // "originalPath": "file:///data/user/0/com.integrasiautama.ospodduta/cache/rn_image_picker_lib_temp_f5afd4ec-a31b-4854-8dc7-5c5dd750e3c6.jpg",
- // "type": "image/jpeg",
- // "uri": "file:///data/user/0/com.integrasiautama.ospodduta/cache/rn_image_picker_lib_temp_10e0c170-69ce-4a1d-8520-82f75945c125.jpg",
- // "width": 768
- // }
- // ]
- // }
-
if (response.assets && response.assets.length > 0) {
- let user_id = user?.id;
- getCoords(loc => {
- // console.log('loc', loc)
- if (loc) {
- let imageObject = {
- "user_id": user_id,
- "drop_point_id": selectedDropPoint?.id,
- "image_uri": response.assets[0].uri,
- "image_blob": response.assets[0].base64,
- "lat": loc.lat,
- "lon": loc.lon
- }
- addOverlay(imageObject)
- }
- else {
+ setLoading(true)
+ try {
+ getCoords(async (loc) => {
+ if (loc) {
+ const timestamp = new Date().toLocaleString();
+ const location = { latitude: loc.lat, longitude: loc.lon };
- }
+ const overlayText = `${timestamp}\nLatitude: ${location.latitude}\nLongitude: ${location.longitude}\nCopyright Nawakara`;
+ const markedImage = await ImageMarker.markText({
+ backgroundImage: {
+ src: { uri: response.assets[0].uri }
+ },
+ watermarkTexts: [{
+ text: overlayText,
+ positionOptions: {
+ position: Position.bottomLeft,
+ },
+ style: {
+ color: '#ffffff',
+ fontSize: 9,
+ fontName: 'Arial',
+ textBackgroundStyle: {
+ padding: 12,
+ type: TextBackgroundType.none,
+ color: '#00000080'
+ }
+ },
+ }],
+ position: 'bottomLeft',
+ color: '#ffffff',
+ fontName: 'Arial-BoldMT',
+ fontSize: 16,
+ scale: 1,
+ });
- })
+ // const tempPath = `file://${RNFS.TemporaryDirectoryPath}/prsensi/${moment().format('YYYYMMDDHHmmss')}.jpg`;
+ // await RNFS.copyFile(markedImage, tempPath);
- }
- }
- });
- }
+ const newImageData = {
+ id: 0,
+ attachment_number: existingAttachmentNumber,
+ imageFile: `file://${markedImage}`,
+ description: "-",
+ data: response.assets[0].uri,
+ type: response.assets[0].type,
+ name: response.assets[0].fileName
+ };
- const addOverlay = async (imageObject) => {
- try {
- const timestamp = new Date().toLocaleString();
- const location = { latitude: imageObject.lat, longitude: imageObject.lon };
-
- const overlayText = `${timestamp}\nLatitude: ${location.latitude}\nLongitude: ${location.longitude}`;
-
- if (imageObject.image_uri) {
- const markedImage = await ImageMarker.markText({
- backgroundImage: {
- src: { uri: imageObject.image_uri }
- },
- watermarkTexts: [{
- text: overlayText,
- positionOptions: {
- position: Position.bottomLeft,
- },
- style: {
- color: '#ffffff',
- fontSize: 8,
- fontName: 'Arial',
- textBackgroundStyle: {
- padding: 10,
- type: TextBackgroundType.none,
- color: '#00000080'
+ const resultImage = await handleUploadImage([newImageData], PATH_PRESENCE);
+ handleSendPresenceData();
}
- },
- }],
- position: 'bottomLeft',
- color: '#ffffff',
- fontName: 'Arial-BoldMT',
- fontSize: 16,
- scale: 1,
- });
- await saveToTemporaryFolder(markedImage, imageObject);
+ });
+ } catch (error) {
+ console.error('Error adding overlay:', error);
+ setLoading(false)
+ }
+ }
}
+ });
+ };
- } catch (error) {
- console.error('Error adding overlay:', error);
- }
- }
- const saveToTemporaryFolder = async (markedImage, imageObject) => {
- try {
- const tempPath = `file://${RNFS.TemporaryDirectoryPath}/presensi/${moment().format('YYYYMMDDHHmmss')}.jpg`;
-
- if (!requestAccessStoragePermission()) {
- return;
- }
- await RNFS.copyFile(markedImage, tempPath);
- const imageBase64 = await RNFS.readFile(tempPath, 'base64');
- storePhoto(imageObject.user_id, imageObject.drop_point_id, tempPath, imageBase64, imageObject.lat, imageObject.lon)
- reloadPhotos();
- } catch (error) {
- console.error('Error saving image to temporary folder:', error);
- }
- }
const handlePresensiCancel = () => {
bottomSheetModal.current?.dismiss();
};
+ const convertDuration = (data) => {
+ const hours = Math.floor(data / 60);
+ const minutes = data % 60;
+ return `${hours} jam ${minutes} menit`;
+ }
+
return (
+
-
+
-
- {strings('home.welcomeMessage')}, Farhan!
+
+ {strings('home.welcomeMessage')}, {user?.name}
-
+
penuh semangat dan produktivitas
@@ -227,15 +399,15 @@ const HomeScreen = ({ route, navigation }) => {
- {moment(currentTime).format('HH:mm')}
+ {moment(currentTime).format('HH:mm')}
{moment(currentTime).format('dddd, DD MMMM - YYYY')}
Masuk
Keluar
- Jam
- Jam
+ {lastPresence && lastPresence.length > 0 ? moment(lastPresence[0].in_time).format('HH:mm') : '-'}
+ {lastPresence && lastPresence.length > 0 && lastPresence[0].out_time ? moment(lastPresence[0].out_time).format('HH:mm') : '-'}
@@ -264,76 +436,95 @@ const HomeScreen = ({ route, navigation }) => {
Lihat Semua
-
- {dummyData.map((item) => (
-
-
-
-
-
-
- {moment(item.date).format('dddd, DD MMMM - YYYY')}
-
-
-
-
- Shift {item.shift}
-
+
+ }
+ >
+ {loadingSkeleton ? (
+ // renderSkeleton(2)
+ null
+ ) : (
+ dataPresence.map((item) => (
+
+
+
+
+
+
+ {moment(item.datePresence).format('dddd, DD MMMM - YYYY')}
+
+
+
+
+ Shift {item.shift}
+
+
-
-
-
-
-
- Durasi Kerja
-
-
-
- Masuk
-
- Keluar
-
-
-
-
- {item.timeIn} Jam
+
+
+
+
+ Durasi Kerja
+
+
+
+ Masuk
+
+ Keluar
+
-
- {item.timeIn}
-
- {item.timeOut}
+
+
+ {item.duration ? convertDuration(item.duration) : ''}
+
+
+ {moment(item.in_time).format('HH:mm')}
+
+ {item.out_time ? moment(item.out_time).format('HH:mm') : ''}
+
-
-
-
- ))}
+
+
+ ))
+ )}
- Ingin presensi sekarang?
+ {strings(`presence.${presenceText}`)}
- Semoga harimu menyenangkan dengan penuh produktivitas dan semangat
+ {strings(`presence.${messageText}`)}
+ {loading && (
+
+
+
+ )}
);
};
@@ -356,7 +547,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
borderBottomLeftRadius: 30,
borderBottomRightRadius: 30,
- paddingHorizontal: 20,
+ paddingHorizontal: 10,
paddingVertical: 30,
},
avatar: {
@@ -404,6 +595,17 @@ const styles = StyleSheet.create({
elevation: 4,
backgroundColor: colors.pureWhite
},
+ loading: {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: 'rgba(255, 255, 255, 0.3)', // Transparent white background
+ zIndex: 1,
+ }
});
export default HomeScreen;
diff --git a/src/screens/Profile.js b/src/screens/Profile.js
index 09e7c26..656fa8b 100644
--- a/src/screens/Profile.js
+++ b/src/screens/Profile.js
@@ -1,16 +1,17 @@
import React, { useState } from 'react';
import { Alert, View, ScrollView, StyleSheet, TouchableOpacity, StatusBar } from 'react-native';
-import { Avatar, Text, Appbar, List, Button, Switch, Modal, TouchableRipple } from 'react-native-paper';
+import { Avatar, Text, Appbar, List, Button, Switch, Modal, Dialog, TouchableRipple } from 'react-native-paper';
import AntDesign from 'react-native-vector-icons/AntDesign';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { clearAllState } from '../utils/Auth';
import { strings } from '../utils/i18n';
import person from '../assets/images/courier_man.png'
import { colors } from '../utils/color';
+import { useSelector } from 'react-redux';
const ProfileScreen = ({ navigation }) => {
const [isSwitchOn, setIsSwitchOn] = React.useState(false);
-
+ const { user } = useSelector(state => state.userReducer)
const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn);
const [visible, setVisible] = useState(false);
@@ -18,7 +19,7 @@ const ProfileScreen = ({ navigation }) => {
const showModal = () => setVisible(true);
const hideModal = () => setVisible(false);
-
+ console.log("user", user.join);
return (
<>
@@ -29,13 +30,13 @@ const ProfileScreen = ({ navigation }) => {
-
+
- Farhan
+ {user.name}
- Land Encroachment Prevention Officer
+ {user.join.m_role_name}
@@ -96,7 +97,7 @@ const ProfileScreen = ({ navigation }) => {
Konfirmasi
{strings('profile.signoutMessage')}
-
@@ -48,7 +49,7 @@ export default function ServiceScreen({ route, navigation }) {
Laporan Kegiatan
Wajib mengisi Kegiatan
{ navigation.navigate('ActivityScreen') }}>
- ISI
+ {strings('global.fill')}
@@ -65,7 +66,7 @@ export default function ServiceScreen({ route, navigation }) {
Laporan Harian
Wajib di isi setiap hari
{ navigation.navigate('DailyReportScreen') }}>
- ISI
+ {strings('global.fill')}
@@ -85,7 +86,7 @@ export default function ServiceScreen({ route, navigation }) {
Patroli
Patroli setiap Pos
{ navigation.navigate('PatroliScreen') }}>
- ISI
+ {strings('global.fill')}
@@ -102,7 +103,7 @@ export default function ServiceScreen({ route, navigation }) {
Kehadiran
Lihat riwayat kehadiran
{ navigation.navigate('PresenceScreen') }}>
- ISI
+ {strings('global.see')}
@@ -118,10 +119,10 @@ export default function ServiceScreen({ route, navigation }) {
style={{ width: '100%', height: Dimensions.get('window').height / 12 }}
resizeMode="contain"
/>
- TOMBOL PANIK
- Tekan tombol ini jika terjadi insiden
+ {strings('global.panicButton')}
+ {strings('global.panicButtonMessage')}
{ navigation.navigate('DialogFormIncident') }}>
- TEKAN
+ {strings('global.press')}
diff --git a/src/screens/registerPage/index.js b/src/screens/registerPage/index.js
index fbb0959..0ff6c2f 100644
--- a/src/screens/registerPage/index.js
+++ b/src/screens/registerPage/index.js
@@ -8,34 +8,106 @@ import { launchCamera } from 'react-native-image-picker';
import { requestAccessStoragePermission } from '../../utils/storage';
import { getCoords } from '../../utils/geolocation';
import { useNavigation } from '@react-navigation/native';
-import {
- BottomSheetModal,
- BottomSheetModalProvider,
- BottomSheetView
-} from '@gorhom/bottom-sheet';
+import { BottomSheetModal, BottomSheetModalProvider, BottomSheetView } from '@gorhom/bottom-sheet';
import RNFS from 'react-native-fs';
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker';
import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment';
-
+import uuid from 'react-native-uuid';
+import RequestModule
+ from '../../services/api/request';
+import { PATH_ID } from '../../config/imageFolder';
+import { useSelector } from 'react-redux';
export default function DialogForm() {
+ const request = new RequestModule('project-charter')
+ const requestAssign = new RequestModule('assign-hr-to-project')
const navigation = useNavigation();
+ const { user } = useSelector(state => state.userReducer)
const [images, setImages] = useState([]);
const [selectedImageUri, setSelectedImageUri] = useState(null);
const bottomSheetImage = useRef(null);
const [selectedProject, setSelectedProject] = useState(null);
- const [area, setArea] = useState({ external: false, internal: false })
+ const [listArea, setListArea] = useState([])
+ const [area, setArea] = useState('')
+ const [children, setChildren] = useState([]);
+ const [selectedPost, setSelectedPost] = useState('')
const [showExpDatePicker, setShowExpDatePicker] = useState(false);
const [expDate, setExpDate] = useState('');
+ const [dataListProjectCharters, setDataListProjectCharters] = useState([]);
+ const [shift, setShift] = useState('')
+ const [position, setPosition] = useState('')
+ const [existingAttachmentNumber, setExistingAttachmentNumber] = useState('');
const bottomSheetModal = useRef(null);
+ const bottomSheetModalArea = useRef(null);
+ const bottomSheetModalPost = useRef(null);
+ const bottomSheetModalPosition = useRef(null);
+
const handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present();
}, []);
+
+ const handleOpenSheetArea = useCallback(() => {
+ bottomSheetModalArea.current?.present();
+ }, []);
+
+ const handleOpenSheetPost = useCallback(() => {
+ bottomSheetModalPost.current?.present();
+ }, []);
+
+ const handleOpenSheetPosition = useCallback(() => {
+ bottomSheetModalPosition.current?.present();
+ }, []);
+
+ useEffect(() => {
+ if (existingAttachmentNumber === '') {
+ setExistingAttachmentNumber(uuid.v4())
+ }
+ handleGetProjectCharter()
+ }, [])
+
+
const handleProjectPress = () => {
- navigation.navigate('SearchPage', { dummyData, onSelect: handleProjectSelect });
+ navigation.navigate('SearchPage', { dataListProjectCharters, onSelect: handleProjectSelect });
};
+
+ const handleSendRegisterData = async () => {
+ const payload = {
+ project_charter_id: selectedProject.id,
+ hr_id: user.id,
+ Position: position,
+ Area: area,
+ Post: selectedPost,
+ attachment_number: existingAttachmentNumber
+ };
+ try {
+ const result = await requestAssign.addData(payload);
+ if (result.status === 201) {
+ Toast.show({
+ type: 'success',
+ text1: strings('presence.dataSentSuccessfully'),
+ });
+ navigation.navigate('App');
+ } else {
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.failedSendDataPresence'),
+ });
+ }
+ } catch (error) {
+ console.error("Network error sending presence data:", error);
+ Toast.show({
+ type: 'error',
+ text1: strings('presence.errorMessage'),
+ });
+ } finally {
+ setLoading(false); // Ensure setLoading is called once in the end
+ }
+ };
+
+
const handleProjectSelect = (project) => {
+ handleGetDataManPowers(project.id)
setSelectedProject(project);
};
@@ -54,43 +126,65 @@ export default function DialogForm() {
bottomSheetImage.current?.present();
}, []);
- const manpower = [
- { id: '2', status: "Sakit", position: "Guard", name: "ibnu", date: '2024-02-10', timeIn: '08:15', timeOut: '17:30' },
- { id: '3', status: "Cuti", position: "Guard", name: "ardhi", date: '2024-02-11', timeIn: '08:10', timeOut: '17:20' },
- { id: '4', status: "", position: "Admin", name: "hana", date: '2024-02-12', timeIn: '08:10', timeOut: '17:20' },
- { id: '5', status: "", position: "Guard", name: "satori", date: '2024-02-13', timeIn: '08:10', timeOut: '17:20' },
- { id: '6', status: "", position: "Guard", name: "khaidir", date: '2024-02-14', timeIn: '08:10', timeOut: '17:20' },
- ];
-
- const dummyData = [
- { id: '1', name: 'MRT', icn: 'test1', sicn: 'test"1' },
- { id: '2', name: 'PLN', icn: 'test1', sicn: 'test"1' },
- { id: '3', name: 'Pertamina', icn: 'test1', sicn: 'test"1' },
- { id: '4', name: 'MD Entertaiment', icn: 'test1', sicn: 'test"1' },
- { id: '5', name: 'Jhonny', icn: 'test1', sicn: 'test"1' },
- { id: '6', name: 'AIA', icn: 'test1', sicn: 'test"1' },
- { id: '7', name: 'PLN Pulogadung', icn: 'test1', sicn: 'test"1' },
- { id: '8', name: 'Google', icn: 'test1', sicn: 'test"1' },
- { id: '9', name: 'Amazon', icn: 'test1', sicn: 'test"1' },
- { id: '10', name: 'Microsoft', icn: 'test1', sicn: 'test"1' },
- { id: '11', name: 'Facebook', icn: 'test1', sicn: 'test"1' },
- { id: '12', name: 'Apple', icn: 'test1', sicn: 'test"1' },
- { id: '13', name: 'Tesla', icn: 'test1', sicn: 'test"1' },
- { id: '14', name: 'Netflix', icn: 'test1', sicn: 'test"1' },
- { id: '15', name: 'Twitter', icn: 'test1', sicn: 'test"1' },
- { id: '16', name: 'LinkedIn', icn: 'test1', sicn: 'test"1' },
- { id: '17', name: 'Instagram', icn: 'test1', sicn: 'test"1' },
- { id: '18', name: 'Snapchat', icn: 'test1', sicn: 'test"1' },
- { id: '19', name: 'WhatsApp', icn: 'test1', sicn: 'test"1' },
- { id: '20', name: 'Uber', icn: 'test1', sicn: 'test"1' },
- { id: '21', name: 'Airbnb', icn: 'test1', sicn: 'test"1' },
- { id: '22', name: 'Spotify', icn: 'test1', sicn: 'test"1' },
- { id: '23', name: 'Pinterest', icn: 'test1', sicn: 'test"1' },
- { id: '24', name: 'Reddit', icn: 'test1', sicn: 'test"1' },
- { id: '25', name: 'Dropbox', icn: 'test1', sicn: 'test"1' },
- { id: '26', name: 'Zoom', icn: 'test1', sicn: 'test"1' },
- { id: '27', name: 'Etsy' }
- ];
+ const handleGetProjectCharter = async () => {
+ const payload = {
+ paging: { start: 0, length: -1 },
+ columns: [
+ { name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND', table_name: '' },
+ { name: 'phase', logic_operator: 'like', operator: 'AND', table_name: '', value: 'Active' },
+ { name: 'sicn', logic_operator: 'like', value: '', operator: 'AND', table_name: '' }
+ ],
+ joins: [{ name: 'm_regional', column_join: 'regional', column_results: ['abbreviation'] }],
+ orders: { columns: ['created_at', 'id'], ascending: false }
+ };
+
+ const result = await request.getDataSearch(payload);
+ if (result && result.status === 200) {
+ let integratedData = result.data.data.map(async (el) => {
+ el.sicnonly = el.sicn
+ if (el.sicn)
+ el.sicn = el.sicn + " (" + el.project_name + ")";
+ return el;
+ })
+ setDataListProjectCharters(result.data.data)
+ } else {
+
+ }
+ }
+
+ const handleSelectArea = (area) => {
+ if (area !== '') {
+ setArea(area.location_name)
+ }
+ setChildren(area.children || []);
+ setSelectedPost(null)
+ bottomSheetModalArea.current?.dismiss();
+ };
+
+ const handleSelectShift = (shift) => {
+ setShift(shift)
+ bottomSheetModal.current?.dismiss();
+ };
+
+ const handleSelectPost = (post) => {
+ setSelectedPost(post)
+ bottomSheetModalPost.current?.dismiss();
+ };
+
+ const handleSelectPosition = (post) => {
+ setPosition(post)
+ bottomSheetModalPosition.current?.dismiss();
+ };
+
+ const handleGetDataManPowers = async (projectCharterId) => {
+ const result = await request.getDataById(`${projectCharterId}?q=area&t=area`);
+ if (result.data.code == 200) {
+ setListArea(result.data.data)
+ } else {
+
+ }
+ };
+
const handleSetImageUri = (uri, description) => {
const newImage = { uri: uri, description: description || "" };
@@ -124,99 +218,102 @@ export default function DialogForm() {
}
};
+ const handleUploadImage = async (dataImage, imageCategory) => {
+ const formData = new FormData();
+ for (const { attachment_number, imageFile, type, name, data, description } of dataImage) {
+ formData.append('attachment_number', attachment_number);
+ formData.append('description', description);
+ formData.append('files', {
+ uri: imageFile,
+ type: type,
+ name: name
+ });
+
+ }
+ const result = await request.uploadImage(imageCategory, formData);
+ }
+
const handleLaunchCamera = () => {
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
- cameraType: 'front',
+ cameraType: 'back',
maxWidth: 768,
maxHeight: 1024,
saveToPhotos: false,
includeBase64: true
};
- launchCamera(options, (response) => {
+ launchCamera(options, async (response) => {
if (response.didCancel) {
+ console.log('User cancelled image picker');
} else if (response.error) {
- } else if (response.customButton) {
+ console.log('ImagePicker Error: ', response.error);
} else {
if (response.assets && response.assets.length > 0) {
- handleSetImageUri(response.assets[0].uri);
- getCoords(loc => {
- if (loc) {
- let imageObject = {
- "image_uri": response.assets[0].uri,
- "image_blob": response.assets[0].base64,
- "lat": loc.lat,
- "lon": loc.lon
- }
- addOverlay(imageObject)
- }
- })
- }
- }
- });
- }
+ setLoading(true)
+ try {
+ getCoords(async (loc) => {
+ if (loc) {
+ const timestamp = new Date().toLocaleString();
+ const location = { latitude: loc.lat, longitude: loc.lon };
- const addOverlay = async (imageObject) => {
- try {
- const timestamp = new Date().toLocaleString();
- const location = { latitude: imageObject.lat, longitude: imageObject.lon };
-
- const overlayText = `${timestamp}\nLatitude: ${location.latitude}\nLongitude: ${location.longitude}`;
-
- if (imageObject.image_uri) {
- const markedImage = await ImageMarker.markText({
- backgroundImage: {
- src: { uri: imageObject.image_uri }
- },
- watermarkTexts: [{
- text: overlayText,
- positionOptions: {
- position: Position.bottomLeft,
- },
- style: {
- color: '#ffffff',
- fontSize: 8,
- fontName: 'Arial',
- textBackgroundStyle: {
- padding: 10,
- type: TextBackgroundType.none,
- color: '#00000080'
- }
- },
- }],
- position: 'bottomLeft',
- color: '#ffffff',
- fontName: 'Arial-BoldMT',
- fontSize: 16,
- scale: 1,
- });
- await saveToTemporaryFolder(markedImage, imageObject);
- }
+ const overlayText = `${timestamp}\nLatitude: ${location.latitude}\nLongitude: ${location.longitude}\nCopyright Nawakara`;
+ const markedImage = await ImageMarker.markText({
+ backgroundImage: {
+ src: { uri: response.assets[0].uri }
+ },
+ watermarkTexts: [{
+ text: overlayText,
+ positionOptions: {
+ position: Position.bottomLeft,
+ },
+ style: {
+ color: '#ffffff',
+ fontSize: 9,
+ fontName: 'Arial',
+ textBackgroundStyle: {
+ padding: 12,
+ type: TextBackgroundType.none,
+ color: '#00000080'
+ }
+ },
+ }],
+ position: 'bottomLeft',
+ color: '#ffffff',
+ fontName: 'Arial-BoldMT',
+ fontSize: 16,
+ scale: 1,
+ });
- } catch (error) {
- console.error('Error adding overlay:', error);
- }
- }
+ // const tempPath = `file://${RNFS.TemporaryDirectoryPath}/prsensi/${moment().format('YYYYMMDDHHmmss')}.jpg`;
+ // await RNFS.copyFile(markedImage, tempPath);
- const saveToTemporaryFolder = async (markedImage, imageObject) => {
- try {
- const tempPath = `file://${RNFS.TemporaryDirectoryPath}/KTA/${moment().format('YYYYMMDDHHmmss')}.jpg`;
+ const newImageData = {
+ id: 0,
+ attachment_number: existingAttachmentNumber,
+ imageFile: `file://${markedImage}`,
+ description: "-",
+ data: response.assets[0].uri,
+ type: response.assets[0].type,
+ name: response.assets[0].fileName
+ };
- if (!requestAccessStoragePermission()) {
- return;
+ const resultImage = await handleUploadImage([newImageData], PATH_ID);
+ handleSendRegisterData();
+ }
+ });
+ } catch (error) {
+ console.error('Error adding overlay:', error);
+ setLoading(false)
+ }
+ }
}
- await RNFS.copyFile(markedImage, tempPath);
- const imageBase64 = await RNFS.readFile(tempPath, 'base64');
- storePhoto(imageObject.user_id, imageObject.drop_point_id, tempPath, imageBase64, imageObject.lat, imageObject.lon)
- reloadPhotos();
- } catch (error) {
- console.error('Error saving image to temporary folder:', error);
- }
- }
+ });
+ };
+
const renderImages = useMemo(() => images.map((image, index) => (
<>
@@ -237,6 +334,26 @@ export default function DialogForm() {
>
)), [images]);
+ const renderArea = useMemo(() => listArea?.manpower_planning?.info?.data.map((data, index) => (
+ <>
+ handleSelectArea(data)}>
+
+
+ >
+ )), [listArea]);
+
+ const renderPost = useMemo(() => children?.map((data, index) => (
+ <>
+ handleSelectPost(data.location_name)}>
+
+
+ >
+ )), [children]);
+
return (
<>
@@ -257,7 +374,7 @@ export default function DialogForm() {
editable={false}
label='Pilih Proyek'
placeholder='Pilih Proyek'
- value={selectedProject ? selectedProject.name : ''}
+ value={selectedProject ? selectedProject.project_name : ''}
right={}
/>
@@ -267,7 +384,7 @@ export default function DialogForm() {
style={{ marginTop: 10 }}
dense={true}
editable={false}
- value=''
+ value={shift ? shift : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
@@ -276,12 +393,12 @@ export default function DialogForm() {
right={}
/>
-
+
}
/>
-
+
}
/>
-
+
+
+
+
+ handleSelectPosition("Guard")}>
+ }
+ />
+
+ handleSelectPosition("Danru")}>
+ }
+ />
+
+ handleSelectPosition("spv")}>
+ }
+ />
+
+
+
+
- console.log("pagi")}>
+ handleSelectShift("pagi")}>
}
+ left={props => }
/>
- console.log("siang")}>
+ handleSelectShift("siang")}>
}
+ left={props => }
/>
- console.log("malem")}>
+ handleSelectShift("malem")}>
}
+ left={props => }
/>
+
+
+
+ {renderArea}
+
+
+
+
+
+ {renderPost}
+
+
+
response,
+ async (error) => {
+ // const originalRequest = error.config;
+ if (error.response.status === 307 || error.response.status === 403) {
+ console.log(error.response);
+ }
+
+ return Promise.reject(error);
+ }
+ );
+
+ return instance;
+ }
+
+ static Request() {
+ const { user } = store.getState().userReducer
+ let token = ''
+ if (user && user.token) {
+ token = user.token
+ }
let instance = axios.create({
headers: {
'Content-Type': 'application/json',
@@ -26,8 +54,11 @@ export default class RequestApi {
}
static RequestUploadImage() {
- const token = localStorage.getItem('token')
-
+ const { user } = store.getState().userReducer
+ let token = ''
+ if (user && user.token) {
+ token = user.token
+ }
let instance = axios.create({
headers: {
"Content-Type": "multipart/form-data",