Browse Source

feat(activity project-activity): activity Page

master
farhantock 7 months ago
parent
commit
9e669abe5d
  1. 322
      src/screens/activity/activity/dialogForm.js
  2. 127
      src/screens/activity/activity/index.js

322
src/screens/activity/activity/dialogForm.js

@ -0,0 +1,322 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { StatusBar } from 'react-native';
import { Button, IconButton, Text, Appbar, TextInput, TouchableRipple, Card, List } from 'react-native-paper';
import { StyleSheet, View, ScrollView, PermissionsAndroid, Image } from 'react-native';
import { colors } from '../../../utils/color';
import { strings } from '../../../utils/i18n';
import { launchCamera } from 'react-native-image-picker';
import { requestAccessStoragePermission } from '../../../utils/storage';
import { getCoords } from '../../../utils/geolocation';
import {
BottomSheetModal,
BottomSheetModalProvider,
BottomSheetView
} from '@gorhom/bottom-sheet';
import RNFS from 'react-native-fs';
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker';
import moment from 'moment';
export default function DialogForm({ route, navigation }) {
const [images, setImages] = useState([]);
const [selectedImageUri, setSelectedImageUri] = useState(null);
const bottomSheetModal = useRef(null);
const bottomSheetImage = useRef(null);
const handleSetImageUri = (uri, description) => {
const newImage = { uri: uri, description: description || "" };
setImages(prevImages => [...prevImages, newImage]);
}
const handleDeleteImage = (index) => {
const updatedImages = [...images];
updatedImages.splice(index, 1);
setImages(updatedImages);
};
const handleOpenSheetImage = useCallback((uri) => {
setSelectedImageUri(uri);
bottomSheetImage.current?.present();
}, []);
const handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present();
}, []);
const handleTakePicture = 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 handleLaunchCamera = () => {
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
cameraType: 'front',
maxWidth: 768,
maxHeight: 1024,
saveToPhotos: false,
includeBase64: true
};
launchCamera(options, (response) => {
if (response.didCancel) {
} else if (response.error) {
} else if (response.customButton) {
} 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)
}
})
}
}
});
}
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);
}
} catch (error) {
console.error('Error adding overlay:', error);
}
}
const saveToTemporaryFolder = async (markedImage, imageObject) => {
try {
const tempPath = `file://${RNFS.TemporaryDirectoryPath}/activity/${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 renderImages = useMemo(() => images.map((image, index) => (
<>
<View key={index} style={styles.imageBlock}>
<TouchableRipple onPress={() => { handleOpenSheetImage(image.uri) }}>
<View style={styles.imageContainer}>
<Image
source={{ uri: image.uri }}
style={styles.image}
resizeMode="cover"
/>
</View>
</TouchableRipple>
</View>
<Button icon="delete" style={{ borderRadius: 10, backgroundColor: colors.semiRed, marginHorizontal: 5, marginBottom: 10 }} textColor={colors.beanRed} mode="contained-tonal" onPress={() => handleDeleteImage(index)}>
{strings('global.delete')}
</Button>
</>
)), [images]);
return (
<>
<StatusBar backgroundColor={colors.blue} barStyle='light-content' translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.blue, elevation: 4 }}>
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('activity.activity')} />
</Appbar.Header>
<View style={{ flex: 1, backgroundColor: colors.pureWhite }}>
<View style={styles.container}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
value=''
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Aktivitas'
placeholder='Aktivitas Yang dijalankan'
/>
<TextInput
style={{ marginTop: 10 }}
dense={true}
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
label="Deskripsi"
mode="outlined"
multiline={true}
numberOfLines={4}
/>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Button icon="camera-plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, paddingVertical: 20, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleTakePicture}>
{strings('global.addImage')}
</Button>
</View>
<ScrollView style={[styles.scrollViewContainer, { backgroundColor: colors.pureWhite }]}>
{renderImages}
</ScrollView>
</View >
<View style={styles.buttonContainer}>
<Button mode="outlined" style={styles.button} textColor={colors.mistBlue} onPress={() => { navigation.goBack() }} >
Kembali
</Button>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.blue }]} onPress={() => console.log('Handle Saved')}>
Simpan
</Button>
</View>
</View >
<BottomSheetModalProvider>
<BottomSheetModal
ref={bottomSheetModal}
index={0}
snapPoints={['20%']}
bottomInset={10}
detached={true}
style={{ marginHorizontal: 15 }}
>
<BottomSheetView >
<TouchableRipple onPress={() => console.log("Open")}>
<List.Item
title="OPEN"
left={props => <List.Icon {...props} icon="" />}
/>
</TouchableRipple>
<TouchableRipple onPress={() => console.log("Close")}>
<List.Item
title="CLOSE"
left={props => <List.Icon {...props} icon="" />}
/>
</TouchableRipple>
</BottomSheetView>
</BottomSheetModal>
<BottomSheetModal
ref={bottomSheetImage}
index={0}
snapPoints={['50%']}
>
<BottomSheetView style={{ alignItems: 'center' }} >
<Image
source={{ uri: selectedImageUri }}
style={styles.imageDetail}
resizeMode="cover"
/>
</BottomSheetView>
</BottomSheetModal>
</BottomSheetModalProvider>
</>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginHorizontal: 10
},
scrollViewContainer: {
flex: 1,
marginVertical: 10,
},
card: {
flex: 1,
marginHorizontal: 8,
marginVertical: 5,
backgroundColor: colors.pureWhite,
},
subText: {
fontSize: 12,
color: colors.blue
},
button: {
flex: 1,
margin: 5,
borderRadius: 5
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
paddingBottom: 20,
backgroundColor: colors.pureWhite,
},
imageBlock: {
marginBottom: 10,
alignItems: 'center',
},
imageContainer: {
flexDirection: 'row',
alignItems: 'center',
},
image: {
width: 330,
height: 150,
borderRadius: 7
},
imageDetail: {
width: 330,
height: 300,
borderRadius: 7
}
})

127
src/screens/activity/activity/index.js

@ -0,0 +1,127 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { Card, Text, TouchableRipple, Button, Appbar, List } from 'react-native-paper';
import { RefreshControl, StyleSheet, View, ScrollView, StatusBar } from 'react-native';
import { colors } from '../../../utils/color';
import Icon from 'react-native-vector-icons/AntDesign';
import { strings } from '../../../utils/i18n';
import moment from 'moment';
import 'moment/locale/id';
moment.locale('id');
import {
BottomSheetModal,
BottomSheetModalProvider,
BottomSheetView
} from '@gorhom/bottom-sheet';
export default function ActivityScreen({ route, navigation }) {
const bottomSheetModal = useRef(null);
const data = [
{ id: 1, position: "Manager", date: "2024-02-09", startTime: "07:03", title: "Pengecekan Kendaraan Karyawan", description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce tincidunt fringilla lobortis. Morbi suscipit massa mollis porttitor fringilla. Integer rutrum ipsum lorem, ut convallis nisl consequat nec. Proin vel elit vitae sapien convallis molestie. Donec id feugiat lectus. Aenean ut dui ut mi semper facilisis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam luctus convallis tellus, quis malesuada felis viverra mattis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aliquam erat volutpat. Mauris eu posuere sapien. Aliquam in nisi at tortor faucibus interdum eget non quam. Aenean non elit ut leo malesuada porttitor.", visitor: "Farhan" },
];
const handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present();
}, []);
return (
<View style={styles.container}>
<StatusBar backgroundColor={colors.blue} barStyle='light-content' translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.blue, elevation: 4 }}>
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('activity.activity')} />
</Appbar.Header>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleOpenSheet} >
{strings('activity.addActivity')}
</Button>
</View>
<ScrollView style={{ backgroundColor: colors.pureWhite }}>
{data.map((item) => (
<TouchableRipple onPress={() => { navigation.navigate('DialogFormActivity') }}>
<Card style={styles.card}>
<Card.Cover source={{ uri: 'https://picsum.photos/700' }} style={{ height: 130 }} resizeMode='cover' />
<Card.Content style={{ marginTop: 10 }}>
<View style={styles.row}>
<Icon name="calendar" size={20} color={colors.amethystSmoke} />
<Text style={{ color: colors.amethystSmoke, fontWeight: 'bold', paddingLeft: 3 }}>
{moment(item.date).format('dddd, DD MMMM - YYYY')} : {item.startTime}
</Text>
</View>
<Text variant="titleLarge" style={{ color: colors.black, fontWeight: '695' }}>{item.title}</Text>
<Text variant="bodyMedium" style={{ color: colors.black, fontWeight: '695' }}>Deskripsi</Text>
<Text variant="bodyMedium" style={{ color: colors.amethystSmoke, textAlign: 'justify' }}>{item.description}</Text>
</Card.Content>
</Card>
</TouchableRipple>
))}
</ScrollView>
<BottomSheetModalProvider>
<BottomSheetModal
ref={bottomSheetModal}
index={0}
snapPoints={['20%']}
bottomInset={5}
detached={true}
style={{ marginHorizontal: 5 }}
>
<BottomSheetView>
<View style={{ justifyContent: 'center' }}>
<Text variant='titleMedium' style={[styles.boldText, { textAlign: 'center', color: colors.black }]}>{strings('activity.activity')} </Text>
</View>
<View style={styles.bottom}>
<TouchableRipple onPress={() => { navigation.navigate('DialogFormActivity') }}>
<List.Item
title="Aktivitas Statis"
description="Menjaga Pos"
left={() => <List.Icon color={colors.blue} icon="home-account" />}
/>
</TouchableRipple>
<TouchableRipple onPress={() => { navigation.navigate('DialogFormPatroli') }}>
<List.Item
title="Aktivitas Dinamis"
description="Melakukan Patroli"
left={() => <List.Icon color={colors.green} icon="walk" />}
/>
</TouchableRipple>
</View>
{/* onPress={() => { navigation.navigate('DialogFormActivity') }} */}
</BottomSheetView>
</BottomSheetModal>
</BottomSheetModalProvider>
</View >
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
backgroundColor: colors.pureWhite
},
card: {
flex: 1,
marginTop: 20,
marginHorizontal: 8,
marginVertical: 5,
backgroundColor: colors.pureWhite,
},
row: {
flexDirection: 'row',
justifyContent: 'flex-start',
paddingVertical: 5,
},
bottom: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 10,
},
shift: {
backgroundColor: colors.semiBlue,
borderRadius: 10,
padding: 5,
paddingHorizontal: 10
},
})
Loading…
Cancel
Save