From 9dca055585e4810b0e13a9830f56052ac0d92fc0 Mon Sep 17 00:00:00 2001 From: farhantock Date: Tue, 18 Jun 2024 13:45:28 +0700 Subject: [PATCH] update --- src/screens/Home.js | 586 ++++++++++++++++++++---------- src/screens/Profile.js | 15 +- src/screens/Service.js | 17 +- src/screens/registerPage/index.js | 439 +++++++++++++++------- src/services/api/base.js | 39 +- 5 files changed, 753 insertions(+), 343 deletions(-) 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 @@ -65,7 +66,7 @@ export default function ServiceScreen({ route, navigation }) { Laporan Harian Wajib di isi setiap hari @@ -85,7 +86,7 @@ export default function ServiceScreen({ route, navigation }) { Patroli Patroli setiap Pos @@ -102,7 +103,7 @@ export default function ServiceScreen({ route, navigation }) { Kehadiran Lihat riwayat kehadiran @@ -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')} 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",