Browse Source

feat(activity project-personel): personel page

master
farhantock 5 months ago
parent
commit
9e6382d303
  1. 176
      src/screens/activity/personel/index.js
  2. 458
      src/screens/activity/projectAset/post/dialogForm.js

176
src/screens/activity/personel/index.js

@ -0,0 +1,176 @@
import { TouchableRipple, Button, Card, Text, Avatar, List, Appbar } from 'react-native-paper';
import { View, StyleSheet, ScrollView } from 'react-native';
import React, { useState } from 'react';
import { colors } from '../../../utils/color'
import Icon from 'react-native-vector-icons/AntDesign';
import moment from 'moment';
import 'moment/locale/id';
import person from '../../../assets/images/courier_man.png'
import { strings } from '../../../utils/i18n';
moment.locale('id');
export default function PersonelScreen({ route, navigation }) {
const role = 'Danru'
const dummyData = [
{ 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 getCardColor = (status) => {
switch (status) {
case 'Sakit':
return colors.mistBlue;
case 'Alpa':
return colors.semiRed;
case 'Izin':
return colors.semiYellow;
case 'Cuti':
return colors.amethystSmoke;
default:
return colors.mercury;
}
};
const getTextColor = (status) => {
switch (status) {
case 'Sakit':
return colors.white;
case 'Alpa':
return colors.beanRed;
case 'Izin':
return colors.orange;
case 'Cuti':
return colors.white;
default:
return colors.mistBlue;
}
};
const getTextStatusColor = (status) => {
switch (status) {
case 'Sakit':
return colors.mistBlue;
case 'Alpa':
return colors.beanRed;
case 'Izin':
return colors.orange;
case 'Cuti':
return colors.amethystSmoke;
default:
return colors.mercury;
}
};
return (
<View style={styles.container}>
<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.personel')} />
</Appbar.Header>
{role === 'Danru' ?
<ScrollView style={{ flex: 1, marginHorizontal: 5 }}>
{dummyData.map((item) => (
<List.Section>
<List.Accordion
titleStyle={{ color: getTextColor(item.status) }}
title={`${item.name} - ${item.position} ${item.status}`} // Added status to the title
style={{ backgroundColor: getCardColor(item.status), borderRadius: 10 }}
left={() => <Avatar.Image size={28} source={person} style={{ marginRight: 5, marginLeft: 10 }} />}>
<View style={styles.row}>
<Button mode="contained" compact={true} style={{ marginHorizontal: 1, backgroundColor: colors.black, borderRadius: 5, flex: 1, }} onPress={() => console.log('Handle Saved')}>
sakit
</Button>
<Button mode="contained" compact={true} textColor={colors.orange} style={{ marginHorizontal: 1, backgroundColor: colors.semiYellow, borderRadius: 5, flex: 1, }} onPress={() => console.log('Handle Saved')}>
Izin
</Button>
<Button mode="contained" compact={true} style={{ marginHorizontal: 1, backgroundColor: colors.amethystSmoke, borderRadius: 5, flex: 1, }} onPress={() => console.log('Handle Saved')}>
Cuti
</Button>
<Button mode="contained" compact={true} style={{ marginHorizontal: 1, backgroundColor: colors.beanRed, borderRadius: 5, flex: 1, }} onPress={() => console.log('Handle Saved')}>
Alpa
</Button>
<Button mode="contained" compact={true} style={{ marginHorizontal: 1, backgroundColor: colors.mercury, borderRadius: 5, flex: 1, }} onPress={() => console.log('Handle Saved')}>
Batal
</Button>
</View>
</List.Accordion>
</List.Section>
))}
</ScrollView>
: <ScrollView style={{ flex: 1, marginHorizontal: 5 }}>
{dummyData.map((item) => (
<List.Section>
<List.Item
titleStyle={{ color: getTextColor(item.status) }}
title={`${item.name} - ${item.position} ${item.status}`} // Added status to the title
style={{ backgroundColor: getCardColor(item.status), borderRadius: 10 }}
left={() => <Avatar.Image size={28} source={person} style={{ marginRight: 5, marginLeft: 10 }} />}
right={() => {
if (item.status !== "") {
return (
<View style={[styles.status, { backgroundColor: colors.pureWhite }]}>
<Text style={{ color: getTextStatusColor(item.status), fontWeight: 'bold', paddingLeft: 3 }}>
{item.status}
</Text>
</View>
);
} else { return null; }
}}
>
</List.Item>
</List.Section>
))}
</ScrollView>}
<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 >
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
backgroundColor: colors.pureWhite
},
status: {
backgroundColor: colors.semiBlue,
borderRadius: 10,
padding: 5,
},
row: {
flexDirection: 'row',
paddingLeft: 0,
marginVertical: 5,
},
card: {
marginHorizontal: 10,
marginVertical: 4,
elevation: 4,
},
button: {
flex: 1,
margin: 5,
borderRadius: 5
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
paddingBottom: 20,
backgroundColor: colors.pureWhite,
},
});

458
src/screens/activity/projectAset/post/dialogForm.js

@ -0,0 +1,458 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { StatusBar } from 'react-native';
import { Button, IconButton, Text, Appbar, TextInput, TouchableRipple, Card, List, Searchbar, RadioButton } from 'react-native-paper';
import { StyleSheet, View, ScrollView, PermissionsAndroid, Image } from 'react-native';
import Icon from 'react-native-vector-icons/AntDesign';
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,
BottomSheetScrollView
} 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';
export default function DialogForm({ route, navigation }) {
const [images, setImages] = useState([]);
const [equipment, setEquipment] = useState([]);
const [selectedImageUri, setSelectedImageUri] = useState(null);
const [filter, setFilter] = useState('')
const bottomSheetModal = useRef(null);
const bottomSheetImage = useRef(null);
const [showDamageDatePicker, setShowDamageDatePicker] = useState(false);
const dummyData = [
{ name: 'HT', description: "", status: "", damageDate: "" },
{ name: 'Borgol', description: "", status: "", damageDate: "" },
{ name: 'Mobil', description: "", status: "", damageDate: "" },
{ name: 'Senter', description: "", status: "", damageDate: "" },
{ name: 'Traffic cone (stick)', description: "", status: "", damageDate: "" },
{ name: 'Laptop', description: "", status: "", damageDate: "" },
];
const handleSelectEquipment = (data) => {
setEquipment(prevEquipment => {
if (prevEquipment.some(item => item.name === data.name)) {
return prevEquipment;
}
return [...prevEquipment, data];
});
setFilter('')
bottomSheetModal.current?.dismiss();
};
const handleStatus = (data, newStatus) => {
setEquipment(prevEquipment => {
return prevEquipment.map(item => {
if (item.name === data) {
return { ...item, status: newStatus };
}
return item;
});
});
}
const handleDamageDateChange = (data, selectedDate) => {
const currentDate = selectedDate;
setShowDamageDatePicker(false);
setEquipment(prevEquipment => {
return prevEquipment.map(item => {
if (item.name === data) {
return { ...item, damageDate: currentDate };
}
return item;
});
});
};
const showDamageDatePickerFunc = () => {
setShowDamageDatePicker(true);
};
console.log("equipment", equipment);
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 handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present();
}, []);
const handleOpenSheetImage = useCallback((uri) => {
setSelectedImageUri(uri);
bottomSheetImage.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}/equipment/${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]);
const renderEquipment = (data) => (
<View>
<TouchableRipple key={data.name} onPress={() => handleSelectEquipment(data)}>
<List.Item
title={data.name}
/>
</TouchableRipple>
</View>
);
const filteredData = dummyData.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()));
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.ppe')} />
</Appbar.Header>
<View style={{ flex: 1, backgroundColor: colors.pureWhite }}>
<View style={styles.container}>
<TouchableRipple onPress={handleOpenSheet}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
editable={false}
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
label="Pilih Perlengkapan Personel"
mode="outlined"
/>
</TouchableRipple>
<Text variant="titleSmall" style={{ marginTop: 10, marginLeft: 10 }}>{strings('activity.ppeUse')}</Text>
<ScrollView style={[styles.scrollViewContainer, { backgroundColor: colors.pureWhite }]}>
{equipment.map(item =>
<List.Accordion
titleStyle={{ color: colors.blue }}
rippleColor={colors.semiBlue}
title={item.name}
style={styles.accordion}
>
<View style={{ marginHorizontal: 10, marginVertical: 5 }}>
<View style={{ flexDirection: 'row', marginHorizontal: 10, marginVertical: 5 }}>
<RadioButton
status={item.status === 'Baik' ? 'checked' : 'unchecked'}
onPress={() => handleStatus(item.name, 'Baik')}
color={colors.blue}
uncheckedColor={colors.amethystSmoke}
/>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleStatus(item.name, 'Baik')}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
Baik
</Text>
</TouchableRipple>
<RadioButton
status={item.status === 'Rusak' ? 'checked' : 'unchecked'}
onPress={() => handleStatus(item.name, 'Rusak')}
color={colors.blue}
uncheckedColor={colors.amethystSmoke}
/>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleStatus(item.name, 'Rusak')}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
Rusak
</Text>
</TouchableRipple>
</View>
{item.status === 'Rusak' &&
<>
<TouchableRipple rippleColor={colors.pureWhite} onPress={showDamageDatePickerFunc}>
<TextInput
value={item.damageDate ? moment(item.damageDate).format("DD-MM-YYYY") : ''}
dense={true}
editable={false}
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
label="Tanggal kerusakan"
mode="outlined"
/>
</TouchableRipple>
<TextInput
style={{ marginTop: 10 }}
dense={true}
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
label="Deskripsi"
mode="outlined"
multiline={true}
numberOfLines={5}
/>
</>
}
{showDamageDatePicker && (
<DateTimePicker
value={item.damageDate || new Date()}
mode="date"
display="default"
onChange={handleDamageDateChange(item.name)}
/>
)}
{renderImages}
</View>
</List.Accordion>
)}
</ScrollView>
<Button icon="camera-plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginVertical: 10, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleTakePicture}>
{strings('global.addImage')}
</Button>
</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={['50%']}
bottomInset={10}
detached={true}
keyboardBehavior="fillParent"
style={{ marginHorizontal: 15 }}
>
<Searchbar
mode='bar'
placeholder="Cari Peralatan"
onChangeText={text => setFilter(text)}
value={filter}
traileringIconColor={colors.white}
style={{ backgroundColor: colors.white }}
showDivider={true}
elevation={1}
/>
<BottomSheetScrollView contentContainerStyle={styles.scrollView}>
{filteredData.map(item => renderEquipment(item))}
</BottomSheetScrollView>
</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: {
marginVertical: 10,
alignItems: 'center',
},
imageContainer: {
flexDirection: 'row',
alignItems: 'center',
},
image: {
width: 330,
height: 150,
borderRadius: 7
},
imageDetail: {
width: 330,
height: 300,
borderRadius: 7
},
accordion: {
backgroundColor: colors.semiBlue,
color: colors.blue,
marginHorizontal: 10,
marginTop: 10,
borderRadius: 5
},
})
Loading…
Cancel
Save