You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

713 lines
22 KiB

// global.Buffer = require('buffer').Buffer;
import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react';
import { Alert, Animated, Keyboard } from 'react-native';
import {
TextInput,
Button,
PermissionsAndroid,
StatusBar,
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
ScrollView,
FlatList,
Dimensions
} from 'react-native';
import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
import { SelectList } from 'react-native-dropdown-select-list';
import DatePicker from 'react-native-date-picker';
import AsyncStorage from '@react-native-async-storage/async-storage';
import DocumentPicker from "react-native-document-picker";
import NetInfo from "@react-native-community/netinfo";
import RNFS from 'react-native-fs';
import MapLibreGL from '@maplibre/maplibre-react-native';
import { MapView,ShapeSource,Camera,UserLocation,PointAnnotation,FillLayer,LineLayer,VectorSource } from "@maplibre/maplibre-react-native";
import Moment from 'moment';
import { RadioButton } from 'react-native-paper';
import AwesomeAlert from 'react-native-awesome-alerts';
import RNFetchBlob from 'rn-fetch-blob';
import { PJU_API_URL } from "../../urlConfig";
type collectScreenProps = {route: any,navigation: any};
function CollectScreen({route,navigation}: collectScreenProps) {
const apiKey = "JoJs2pcDv5o0yQlQLMfI";
const [basemap, setBasemap] = useState("streets-v2");
const styleURL = `https://api.maptiler.com/maps/${basemap}/style.json?key=${apiKey}`;
const {fixCoordinate} = route.params;
const [imgUrl, setImgUrl] = useState([]);
const [files, setFiles] = useState([]);
const [fileDocument, setFileDocument] = useState([]);
const [currentIndex, setCurrentIndex] = useState(0);
const [connected, setConnected] = useState(true)
const openCameraLib = async () => {
const result = await launchCamera({
mediaType: 'photo',
quality: 1,
includeBase64: true,
saveToPhotos: true
});
const newObj = {
file_type: result?.assets![0]?.type,
file: result?.assets![0]?.base64,
fileSize: result?.assets![0]?.fileSize,
}
setFiles([...files,newObj]);
setImgUrl([...imgUrl,result?.assets![0]?.base64]);
};
const openLib = async () => {
const result = await launchImageLibrary({
quality: 1,
mediaType: 'photo',
selectionLimit: 0,
includeExtra: true,
includeBase64: true
});
result?.assets?.forEach(asset => {
const newObj = {
file_type: asset.type,
file: asset.base64,
fileSize: asset.fileSize
}
setFiles([...files,newObj]);
setImgUrl(oldArray => [...oldArray,asset.base64]);
});
};
const PrevImg = ({isIndex}) => {
if(isIndex===0){
return (
<TouchableOpacity disabled style={{width:'10%',alignSelf:'center'}}>
<Image source={require('../../assets/icon/left-arrow.png')} />
</TouchableOpacity>
)
}
return (
<TouchableOpacity style={{width:'10%',alignSelf:'center'}} onPress={()=> {
setCurrentIndex(currentIndex-1)
ref.current.scrollToIndex({
animated: true,
index: currentIndex-1
});
}}>
<Image source={require('../../assets/icon/left-arrow.png')} />
</TouchableOpacity>
)
};
const NextImg = ({isIndex}) => {
if(imgUrl.length<=1){
return (
<TouchableOpacity disabled style={{width:'10%',alignSelf:'center'}}>
<Image style={{alignSelf:'flex-end'}} source={require('../../assets/icon/right-arrow.png')} />
</TouchableOpacity>
)
}
else{
if (isIndex === (imgUrl.length-1)) {
return (
<TouchableOpacity disabled style={{width:'10%',alignSelf:'center'}}>
<Image style={{alignSelf:'flex-end'}} source={require('../../assets/icon/right-arrow.png')} />
</TouchableOpacity>
)
}
else{
return (
<TouchableOpacity style={{width:'10%',alignSelf:'center'}} onPress={()=> {
setCurrentIndex(currentIndex+1)
ref.current.scrollToIndex({
animated: true,
index: currentIndex+1
});
}}>
<Image style={{alignSelf:'flex-end'}} source={require('../../assets/icon/right-arrow.png')} />
</TouchableOpacity>
)
}
}
};
const PJU_Type = [
{key:'Lampu 20 W', value:'Lampu 20 W'},
{key:'Lampu 15 W', value:'Lampu 15 W'},
{key:'Lampu 10 W', value:'Lampu 10 W'},
{key:'Lampu 5 W', value:'Lampu 5 W'},
]
const ref = useRef();
const [open, setOpen] = useState(false)
const [pju_name, setPJUname] = useState('');
const [selectedPJUType, setselectedPJUType] = useState("");
const [pju_info, setPJUinfo] = useState('');
const [checkedStatus, setCheckedStatus] = useState('');
const [install_date, setInstallDate] = useState(new Date());
const momentDate = Moment(install_date).format('YYYY-MM-DD')
const [attachmentImgError, setattachmentImgError] = useState(false);
const [pjuNameError, setPJUNameError] = useState(false);
const [PJUTypeError, setPJUTypeError] = useState(false);
const [PJUStatusError, setPJUStatusError] = useState(false);
const [PJUInfoError, setPJUInfoError] = useState(false);
const [attachmentSuccess, setAttachmentSuccess] = useState(0)
useEffect(()=> {
navigation.addListener('beforeRemove', (e:any)=> {
console.log("Event",e);
if (e.data.action.type !== "NAVIGATE"){
e.preventDefault();
Alert.alert(
'Unsave Case',
'There are unsave case. Please chose what you want.',
[
{
text: 'Ok',
onPress: () => navigation.dispatch(e.data.action)
},
{
text: 'Continue',
style: 'cancel'
}
]
)
}
})
const unsubscribe = NetInfo.addEventListener((state) => {
setConnected(state.isConnected!)
});
return () => {
unsubscribe();
};
},[]);
const collectDataOnline = async() => {
try {
const result = {
status: 0,
body: {},
}
await RNFetchBlob.config({
trusty : true
}).fetch('POST', `${PJU_API_URL}/addData`,{
"Accept": 'application/json',
"Content-Type": "application/json",
},
JSON.stringify({
nama: pju_name,
jenis: selectedPJUType,
tanggal_installasi: momentDate,
status: checkedStatus,
keterangan: pju_info,
geom: `POINT(${fixCoordinate.long} ${fixCoordinate.lat})`
})
).then(async (resp) => {
const json = resp.json()
const status = resp.respInfo.status;
result.status = status;
result.body = json;
if (status == 201) {
insertAttachment(json,files)
const storeData = async () => {
try {
await AsyncStorage.setItem(json[0].id+" / "+pju_name+" / "+selectedPJUType+" / "+momentDate+" / "+checkedStatus+" / "+pju_info+" / "+fixCoordinate.long+" / "+fixCoordinate.lat+" / sentPJU","Value");
} catch (e) {
}
};
storeData()
}
})
} catch (error) {
}
}
const collectDataOffline = async() => {
const storeData = async () => {
try {
await AsyncStorage.setItem(pju_name+" / "+selectedPJUType+" / "+momentDate+" / "+checkedStatus+" / "+pju_info+" / "+fixCoordinate.long+" / "+fixCoordinate.lat+" / draftPJU","Value");
} catch (e) {
}
};
storeData()
setAlertOffline(false);
navigation.navigate('Survey Penerangan Jalan Umum', { screen: 'Survey Penerangan Jalan Umum'})
}
const getMyObject = async () => {
try {
const jsonValue = await AsyncStorage.getItem('draft')
console.log(JSON.parse(jsonValue!));
// return jsonValue != null ? JSON.parse(jsonValue) : null
} catch(e) {
// read error
}
}
const removeValue = async () => {
try {
await AsyncStorage.removeItem('Test mobile offline / Lampu 20 W / 2024-11-20 / Baik / - / [object Object] / draftPJU')
} catch(e) {
// remove error
}
}
const getAllKeys = async () => {
// let keys = []
try {
const keys = await AsyncStorage.getAllKeys()
console.log(keys);
} catch(e) {
// read key error
}
// example console.log result:
// ['@MyApp_user', '@MyApp_key']
}
const insertAttachment = async (json: any,files: any) => {
try {
files.forEach(async function(file:any) {
const url = `${PJU_API_URL}/addAttachment`;
await RNFetchBlob.config({
trusty : true
}).fetch('POST', url,{
"Content-Type": "application/json",
},
JSON.stringify({
pju_id:json[0].id,
file:file.file,
mimetype:file.file_type,
size:file.fileSize})
).then(async (resp) => {
Alert.alert("Success")
setAlertOnline(false);
navigation.navigate('Survey Penerangan Jalan Umum', { screen: 'Survey Penerangan Jalan Umum'})
}
)
});
} catch (error) {
console.log(error);
}
}
const uploadFileOnHandler = async () => {
try {
const pickedFile = await DocumentPicker.pickSingle({
type: [DocumentPicker.types.allFiles],
// allowMultiSelection:true
})
await RNFS.readFile(pickedFile.uri, 'base64').then(data => {
console.log('base64', data);
let your_bytes = btoa(data);
const newObj = {
file_type: pickedFile.type,
file: your_bytes
}
setFiles([...files,newObj]);
setFileDocument([...fileDocument,pickedFile]);
});
} catch (error) {
console.error(error)
}
}
const deleteByIndex = (index:number) => {
setFileDocument(oldValues => {
return oldValues.filter((_: any, i: number) => i !== index)
})
}
const [alertOnline, setAlertOnline] = useState(false);
const [alertOffline, setAlertOffline] = useState(false);
const showDialog = () => {
// if (imgUrl.length === 0) {
// setattachmentImgError(true);
// }
// else{
// setattachmentImgError(false);
// }
if (!pju_name) {
setPJUNameError(true);
}
else{
setPJUNameError(false);
}
if (!selectedPJUType) {
setPJUTypeError(true);
}
else{
setPJUTypeError(false);
}
if (!checkedStatus) {
setPJUStatusError(true);
}
else{
setPJUStatusError(false);
}
if (!pju_info) {
setPJUInfoError(true);
}
else{
setPJUInfoError(false);
}
if(!pju_name || !pju_info || !selectedPJUType || !checkedStatus) {
return false
}
if(connected){
setAlertOnline(true);
}
else{
setAlertOffline(true)
}
};
const handleCancelAlertOnline = () => {
setAlertOnline(false);
};
const handleCancelAlertOffline = () => {
setAlertOffline(false);
};
return(
<View style={styles.mainContainer}>
<ScrollView style={styles.container}>
<Text style={styles.title}>Form Survey PJU</Text>
<View>
<Text style={styles.label}>Image</Text>
<View style={styles.borderImg}>
<Text style={{justifyContent: 'center', alignItems: 'center', alignSelf: 'center', color: 'black'}}>{imgUrl.length === 0 ? currentIndex :currentIndex+1}/{imgUrl.length}</Text>
<View style={{flexDirection: 'row'}}>
<PrevImg isIndex={currentIndex}/>
<Animated.FlatList
ref={ref}
style={{width:'80%'}}
data={imgUrl}
showsHorizontalScrollIndicator={false}
pagingEnabled
onScroll={e=>{
const x = e.nativeEvent.contentOffset.x;
setCurrentIndex(parseFloat((x/330).toFixed(0)));
}}
horizontal
renderItem={({item,index}) => {
return(
<Animated.Image resizeMode='center' style={{width: 295,
height: 500,
borderRadius: 6,}} source={{uri: `data:image/jpeg;base64,${imgUrl[index]}`}} />
)
}}
/>
<NextImg isIndex={currentIndex}/>
</View>
<View style={styles.btnCameraContainer}>
<TouchableOpacity style={styles.btnCamera} onPress={openCameraLib}>
<Image
source={require('../../assets/icon/camera.png')}
style={styles.buttonImageIconStyle}
/>
{/* <Text>
Open Camera
</Text> */}
</TouchableOpacity>
<TouchableOpacity style={styles.btnCamera} onPress={openLib}>
<Image
source={require('../../assets/icon/gallery.png')}
style={styles.buttonImageIconStyle}
/>
</TouchableOpacity>
</View>
</View>
</View>
<View
style={{
marginTop: 15
}}
/>
<View style={{flexDirection:'column'}}>
<MapView
style={{width:'80%',height:160,alignSelf:'center'}}
mapStyle={styleURL}
rotateEnabled={false}
zoomEnabled={false}
pitchEnabled={false}
attributionPosition={{bottom: 8, right: 8}}
scrollEnabled={false}
>
<PointAnnotation
children={<></>}
id={"pjuCoordinate"}
coordinate={[fixCoordinate.long, fixCoordinate.lat]}
/>
<Camera
centerCoordinate={[fixCoordinate.long, fixCoordinate.lat]!}
pitch={50}
zoomLevel={15}
minZoomLevel={5}
maxZoomLevel={20}
/>
</MapView>
<TouchableOpacity style={{alignSelf:'center',paddingTop:10}} onPress={()=>navigation.navigate('PJU Map Collect', { screen: 'PJU Map Collect'})}>
<View style={{backgroundColor:'#217dd3',flexDirection:'row',width:150,height:30,borderRadius:20,justifyContent:'center'}}>
<Text style={{alignSelf:'center',fontWeight:'bold',fontSize:16,color:'white'}}>Change Location</Text>
</View>
</TouchableOpacity>
</View>
<View
style={{
marginTop: 15
}}
/>
<View>
<Text style={styles.label}>Name<Text style={{color:'red',fontWeight:'bold'}}>*</Text></Text>
<TextInput
style={styles.input}
onChangeText={(text) => {
setPJUname(text)
}}
onChange={()=> {
setPJUNameError(false)
}}
/>
{pjuNameError ? <Text style={styles.errorInput}>Name is Required!</Text>:null}
</View>
<View
style={{
marginTop: 15
}}
/>
<View>
<Text style={styles.label}>PJU Type <Text style={{color:'red',fontWeight:'bold'}}>*</Text></Text>
<SelectList
search={false}
placeholder="Select PJU Type"
boxStyles={{borderRadius:50,borderWidth:2}}
inputStyles={{color:'black'}}
dropdownTextStyles={{color:'black'}}
data={PJU_Type}
setSelected={setselectedPJUType}
onSelect={()=> (
[setPJUTypeError(false)]
)}
/>
{PJUTypeError ? <Text style={styles.errorInput}>PJU Type is Required!</Text>:null}
</View>
<View
style={{
marginTop: 15
}}
/>
<Text style={styles.label}>Status <Text style={{color:'red',fontWeight:'bold'}}>*</Text></Text>
<View style={{flexDirection:'row',alignItems:'center'}}>
<RadioButton
value="Baik"
status={ checkedStatus === 'Baik' ? 'checked' : 'unchecked' }
onPress={() => {setCheckedStatus('Baik'); setPJUStatusError(false)}}
/>
<Text style={{color:"black"}}>Baik</Text>
</View>
<View style={{flexDirection:'row',alignItems:'center'}}>
<RadioButton
value="Rusak"
status={ checkedStatus === 'Rusak' ? 'checked' : 'unchecked' }
onPress={() => {setCheckedStatus('Rusak'); setPJUStatusError(false); }}
/>
<Text style={{color:"black"}}>Rusak</Text>
</View>
{PJUStatusError ? <Text style={styles.errorInput}>Status is Required!</Text>:null}
<View
style={{
marginTop: 15
}}
/>
<View>
<Text style={styles.label}>Information <Text style={{color:'red',fontWeight:'bold'}}>*</Text></Text>
<TextInput style={{borderWidth:1,borderRadius:20,padding: 10,color:'black',textAlignVertical:'top'}}
multiline
numberOfLines={5}
maxLength={255}
onChangeText={(text) => {
setPJUinfo(text)
}}
onChange={()=> {
setPJUInfoError(false)
}}
/>
{PJUInfoError ? <Text style={styles.errorInput}>Information is Required!</Text>:null}
</View>
<View
style={{
marginTop: 15
}}
/>
<View>
<Text style={styles.label}>Installation Date</Text>
<TextInput
style={styles.input}
onPress={() => setOpen(true)}
value={String(momentDate)}
/>
<DatePicker
locale='en'
theme='dark'
mode="date"
modal
open={open}
date={install_date}
onConfirm={(survey_date) => {
setOpen(false)
setInstallDate(survey_date)
}}
onCancel={() => {
setOpen(false)
}}
/>
</View>
<View
style={{
marginTop: 15
}}
/>
<View></View>
</ScrollView>
<Button title='Send' onPress={showDialog} />
{/* <Button title='Get Local' onPress={getMyObject} />
<Button title='Delete' onPress={removeValue} />
<Button title='GetAll' onPress={getAllKeys} /> */}
{/* Online Mode Alert */}
<AwesomeAlert
show={alertOnline}
showProgress={false}
title="Collecting Data"
message="Are you sure for Collecting PJU data?"
closeOnTouchOutside={false}
closeOnHardwareBackPress={false}
showCancelButton={true}
showConfirmButton={true}
cancelText="No, cancel"
confirmText="Yes, Collect it"
confirmButtonColor="#137997"
onCancelPressed={() => {
handleCancelAlertOnline();
}}
onConfirmPressed={() => {
collectDataOnline();
}}
overlayStyle={{width:'100%'}}
contentContainerStyle={{width:'70%',borderRadius:10}}
titleStyle={{fontSize:20}}
messageStyle={{fontSize:16}}
/>
{/* Offline Mode Alert */}
<AwesomeAlert
show={alertOffline}
showProgress={false}
title="You are offline"
message="You want to add data to the draft?"
closeOnTouchOutside={false}
closeOnHardwareBackPress={false}
showCancelButton={true}
showConfirmButton={true}
cancelText="No, cancel"
confirmText="Yes, Add it"
confirmButtonColor="#DD6B55"
onCancelPressed={() => {
handleCancelAlertOffline();
}}
onConfirmPressed={() => {
collectDataOffline();
}}
overlayStyle={{width:'100%'}}
contentContainerStyle={{width:'70%',borderRadius:10}}
titleStyle={{fontSize:20}}
messageStyle={{fontSize:16}}
/>
</View>
)
}
const styles = StyleSheet.create({
errorInput: {
color: 'red',
marginLeft: 20
},
inputLong: {
height: 40,
borderWidth: 1,
padding: 10,
width: 180,
marginRight: 20
},
inputLat: {
height: 40,
borderWidth: 1,
padding: 10,
borderRadius: 50,
width: 180,
color: 'black'
},
input: {
height: 40,
borderWidth: 1,
padding: 10,
borderRadius: 50,
color: 'black'
},
container: {
flex: 1,
backgroundColor: '#ecf0f1',
padding: 10,
},
mainContainer: {
flex: 1,
},
label: {
fontSize: 21,
color: 'black',
// marginBottom: 20
},
title: {
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
fontWeight: 'bold',
fontSize: 22,
color: 'black',
marginBottom: 10
},
borderImg: {
borderColor: 'black',
borderWidth: 1,
borderRadius: 20,
},
img: {
width: 295,
height: 500,
borderRadius: 6,
},
btnCameraContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
top:10,
},
btnCamera: {
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
width: 100,
height: 40,
bottom:20,
borderRadius: 6,
backgroundColor: '#80CBD3'
},
textBtn: {
color: '#fff'
},
buttonImageIconStyle: {
padding: 10,
margin: 5,
height: 25,
width: 25,
resizeMode: 'stretch',
},
});
export default CollectScreen;