farhantock
8 months ago
11 changed files with 1587 additions and 74 deletions
@ -0,0 +1,361 @@ |
|||||||
|
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; |
||||||
|
import { List, TouchableRipple, TextInput, Card, Text, Button } from 'react-native-paper'; |
||||||
|
import { View, StyleSheet, ScrollView } from 'react-native'; |
||||||
|
import DateTimePicker from '@react-native-community/datetimepicker'; |
||||||
|
import moment from 'moment'; |
||||||
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
import { |
||||||
|
BottomSheetModal, |
||||||
|
BottomSheetModalProvider, |
||||||
|
BottomSheetScrollView |
||||||
|
} from '@gorhom/bottom-sheet'; |
||||||
|
import { strings } from '../../../utils/i18n'; |
||||||
|
export default function ChronologyScreen({ route, navigation }) { |
||||||
|
const [chronology, setChronology] = useState('') |
||||||
|
const [incidentRiskPotential, setIncidentRiskPotential] = useState({ impact: "", likelihood: "", value: "", impactDescription: "", likelihoodDescription: "" }) |
||||||
|
const [riskLevel, setRiskLevel] = useState({ level: "", backgroundColor: '', color: '' }) |
||||||
|
const bottomSheetModalImpactRef = useRef(null); |
||||||
|
const bottomSheetModalLikelihoodRef = useRef(null); |
||||||
|
|
||||||
|
const snapPoints = useMemo(() => ['25%', '50%'], []); |
||||||
|
|
||||||
|
const handleOpenImpact = useCallback(() => { |
||||||
|
bottomSheetModalImpactRef.current?.present(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const handleOpenLikelihood = useCallback(() => { |
||||||
|
bottomSheetModalLikelihoodRef.current?.present(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (incidentRiskPotential.impact !== "" && incidentRiskPotential.likelihood !== "") { |
||||||
|
const impactValue = dataSelectImpact.find(item => item.label === incidentRiskPotential.impact)?.value || 0; |
||||||
|
const likelihoodValue = dataSelectLikelihood.find(item => item.label === incidentRiskPotential.likelihood)?.value || 0; |
||||||
|
|
||||||
|
const val = impactValue * likelihoodValue; |
||||||
|
let newRiskLevel, backgroundColor, color; |
||||||
|
if (val >= 1 && val <= 5) { |
||||||
|
backgroundColor = '#E3F8E8' |
||||||
|
color = '#17C13E' |
||||||
|
newRiskLevel = 'Rendah'; |
||||||
|
} else if (val >= 6 && val <= 10) { |
||||||
|
backgroundColor = "#F8DC49" |
||||||
|
color = "white" |
||||||
|
newRiskLevel = 'Sedang'; |
||||||
|
} else if (val >= 11 && val <= 15) { |
||||||
|
backgroundColor = '#FFD9AF' |
||||||
|
color = '#EC9C3D' |
||||||
|
newRiskLevel = 'Tinggi'; |
||||||
|
} else { |
||||||
|
backgroundColor = '#FFC5C3' |
||||||
|
color = '#D9534F' |
||||||
|
newRiskLevel = 'Bencana'; |
||||||
|
} |
||||||
|
|
||||||
|
if (incidentRiskPotential.value !== val.toString() || riskLevel.level !== newRiskLevel || riskLevel.backgroundColor !== backgroundColor || riskLevel.color !== color) { |
||||||
|
setIncidentRiskPotential(prevState => ({ ...prevState, value: val.toString() })); |
||||||
|
setRiskLevel({ level: newRiskLevel, backgroundColor: backgroundColor, color: color }); |
||||||
|
} |
||||||
|
} |
||||||
|
}, [incidentRiskPotential, riskLevel]); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleSelectImpact = (impact) => { |
||||||
|
setIncidentRiskPotential(prevState => ({ |
||||||
|
...prevState, |
||||||
|
impact: impact.label, |
||||||
|
value: impact.value, |
||||||
|
impactDescription: impact.description |
||||||
|
})); |
||||||
|
bottomSheetModalImpactRef.current?.dismiss(); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleSelectLikelihood = (likelihood) => { |
||||||
|
setIncidentRiskPotential(prevState => ({ |
||||||
|
...prevState, |
||||||
|
likelihood: likelihood.label, |
||||||
|
likelihoodDescription: likelihood.description |
||||||
|
})); |
||||||
|
bottomSheetModalLikelihoodRef.current?.dismiss(); |
||||||
|
}; |
||||||
|
|
||||||
|
const dataSelectImpact = [ |
||||||
|
{ label: 'Insignificant', value: 1, description: 'Tidak ada Gangguan Operasional Perusahaan' }, |
||||||
|
{ label: 'Minor', value: 2, description: 'Operasional Perusahaan Terganggu Namun tidak terlalu signifikan dapat berjalan 75%' }, |
||||||
|
{ label: 'Moderate', value: 3, description: 'Operasional Perusahaan Terganggu Namun tidak dapat berjalan 50%' }, |
||||||
|
{ label: 'Major', value: 4, description: 'Operasional Perusahaan Terganggu, Namun tidak dapat berjalan 25%' }, |
||||||
|
{ label: 'Extreme', value: 5, description: 'Operasional Perusahaan Terhenti akibat Gangguan Yang Ada' } |
||||||
|
]; |
||||||
|
|
||||||
|
const dataTranslasiImpact = { |
||||||
|
Insignificant: 'Tidak signifikan', |
||||||
|
Minor: 'Kecil', |
||||||
|
Moderate: 'Sedang', |
||||||
|
Major: 'Besar', |
||||||
|
Extreme: 'Ekstrim', |
||||||
|
} |
||||||
|
|
||||||
|
const dataSelectLikelihood = [ |
||||||
|
{ label: 'Rare', value: 1, description: '< 5%, Terjadi dalam 1x lebih 20 Tahun' }, |
||||||
|
{ label: 'Unlikely', value: 2, description: '5% - 29%, Terjadi dalam 1x dalam setiap 20 Tahun' }, |
||||||
|
{ label: 'Possible', value: 3, description: '30% - 59%, Terjadi dalam 1x dalam setiap 5 Tahun' }, |
||||||
|
{ label: 'Likely', value: 4, description: '60% - 89%, Terjadi dalam 1x dalam setiap tahun' }, |
||||||
|
{ label: 'Almost Certain', value: 5, description: '90%, Terjadi dalam 1x dalam setiap bulan' } |
||||||
|
] |
||||||
|
|
||||||
|
const dataTranslasiLikelihood = { |
||||||
|
Rare: 'Langka', |
||||||
|
Unlikely: 'Jarang', |
||||||
|
Possible: 'Mungkin terjadi', |
||||||
|
Likely: 'Sangat memungkinkan', |
||||||
|
"Almost Certain": 'Sudah pasti', |
||||||
|
} |
||||||
|
|
||||||
|
console.log("incidentRiskPotential", incidentRiskPotential); |
||||||
|
|
||||||
|
const renderImpact = (data) => ( |
||||||
|
<View> |
||||||
|
<TouchableRipple key={dataTranslasiImpact[data.label]} onPress={() => handleSelectImpact(data)}> |
||||||
|
<List.Item |
||||||
|
title={dataTranslasiImpact[data.label]} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
</View> |
||||||
|
); |
||||||
|
const renderLikelihood = (data) => ( |
||||||
|
<View> |
||||||
|
<TouchableRipple key={dataTranslasiLikelihood[data.label]} onPress={() => handleSelectLikelihood(data)}> |
||||||
|
<List.Item |
||||||
|
title={dataTranslasiLikelihood[data.label]} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
</View> |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> |
||||||
|
Kronologis kejadian |
||||||
|
</Text> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> |
||||||
|
Jelaskan bagaimana terjadinya insiden |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
<ScrollView> |
||||||
|
<View style={styles.container}> |
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
underlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
label="Kronologis Singkat Kejadian" |
||||||
|
mode="outlined" |
||||||
|
placeholder='Isi kronologis' |
||||||
|
multiline={true} |
||||||
|
numberOfLines={5} |
||||||
|
value={chronology} |
||||||
|
onChangeText={(text) => { |
||||||
|
setChronology(text) |
||||||
|
}} |
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
underlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label="Penjelasan Permasalahan" |
||||||
|
placeholder='Isi Permasalahan' |
||||||
|
multiline={true} |
||||||
|
numberOfLines={5} |
||||||
|
onChangeText={(text) => { |
||||||
|
setChronology(text) |
||||||
|
}} |
||||||
|
/> |
||||||
|
<TouchableRipple onPress={handleOpenImpact}> |
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
editable={false} |
||||||
|
underlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Impact' |
||||||
|
value={incidentRiskPotential.impact ? dataTranslasiImpact[incidentRiskPotential.impact] : ''} |
||||||
|
right={<TextInput.Icon icon="chevron-down" onPress={handleOpenImpact} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
{incidentRiskPotential.impactDescription && ( |
||||||
|
<Card style={{ backgroundColor: colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<Text variant="bodySmall" style={{ fontSize: 12 }}> |
||||||
|
{incidentRiskPotential.impactDescription} |
||||||
|
</Text> |
||||||
|
</Card> |
||||||
|
)} |
||||||
|
<TouchableRipple onPress={handleOpenLikelihood}> |
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
editable={false} |
||||||
|
underlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Likelihood' |
||||||
|
value={incidentRiskPotential.likelihood ? dataTranslasiLikelihood[incidentRiskPotential.likelihood] : ''} |
||||||
|
right={<TextInput.Icon icon="chevron-down" onPress={handleOpenLikelihood} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
|
||||||
|
{incidentRiskPotential.likelihoodDescription && ( |
||||||
|
<Card style={{ backgroundColor: colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<Text variant="bodySmall" style={{ fontSize: 12 }}> |
||||||
|
{incidentRiskPotential.likelihoodDescription} |
||||||
|
</Text> |
||||||
|
</Card> |
||||||
|
)} |
||||||
|
<View style={styles.row}> |
||||||
|
<Card style={{ flex: 1, marginRight: 5, backgroundColor: '#FFC5C3', paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<Text variant="bodySmall" style={{ fontSize: 12, color: '#D9534F' }}> |
||||||
|
Critical |
||||||
|
</Text> |
||||||
|
</Card> |
||||||
|
<Card style={{ flex: 1, marginLeft: 5, backgroundColor: riskLevel.backgroundColor, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<Text variant="bodySmall" style={{ fontSize: 12, color: riskLevel.color }}> |
||||||
|
{riskLevel.level} {incidentRiskPotential.value} |
||||||
|
</Text> |
||||||
|
</Card> |
||||||
|
</View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14 }}> |
||||||
|
Rencana tindakan pencegahan |
||||||
|
</Text> |
||||||
|
|
||||||
|
<Card elevation={2} style={styles.card}> |
||||||
|
<View style={styles.cardContent}> |
||||||
|
<View style={styles.leftContent}> |
||||||
|
<Icon name="view-list" size={25} color={colors.blue} /> |
||||||
|
</View> |
||||||
|
<View style={styles.midContent}> |
||||||
|
<Text variant="bodySmall" style={styles.subText}>01-03-2024</Text> |
||||||
|
<Text variant="headlineMedium" style={styles.Text}>Farhan</Text> |
||||||
|
<Text variant="bodySmall" style={styles.subText}>Melakukan pengamatan dan patroli area pagar luar Depo MRT</Text> |
||||||
|
</View> |
||||||
|
</View> |
||||||
|
<View style={styles.buttonContainer}> |
||||||
|
<Button mode="outlined" style={styles.button} textColor={colors.beanRed} onPress={() => { navigation.goBack() }} > |
||||||
|
Edit |
||||||
|
</Button> |
||||||
|
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}> |
||||||
|
Hapus |
||||||
|
</Button> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
|
||||||
|
{/* color: (typeRiskIncident === 'Critical' ? '#D9534F' : (typeRiskIncident === 'Major' ? '#EC9C3D' : 'black')) */} |
||||||
|
</View> |
||||||
|
</ScrollView> |
||||||
|
<Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginHorizontal: 8, }} textColor={colors.blue} mode="contained-tonal" onPress={() => { navigation.navigate('ContainedActionScreen') }}> |
||||||
|
{strings('incidentReport.containedAction')} |
||||||
|
</Button> |
||||||
|
<BottomSheetModalProvider> |
||||||
|
<BottomSheetModal |
||||||
|
ref={bottomSheetModalImpactRef} |
||||||
|
index={1} |
||||||
|
snapPoints={snapPoints} |
||||||
|
> |
||||||
|
<BottomSheetScrollView contentContainerStyle={styles.scrollView}> |
||||||
|
{dataSelectImpact.map(item => renderImpact(item))} |
||||||
|
</BottomSheetScrollView> |
||||||
|
</BottomSheetModal> |
||||||
|
</BottomSheetModalProvider> |
||||||
|
<BottomSheetModalProvider> |
||||||
|
<BottomSheetModal |
||||||
|
ref={bottomSheetModalLikelihoodRef} |
||||||
|
index={1} |
||||||
|
snapPoints={snapPoints} |
||||||
|
> |
||||||
|
<BottomSheetScrollView contentContainerStyle={styles.scrollView}> |
||||||
|
{dataSelectLikelihood.map(item => renderLikelihood(item))} |
||||||
|
</BottomSheetScrollView> |
||||||
|
</BottomSheetModal> |
||||||
|
</BottomSheetModalProvider> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
containerModal: { |
||||||
|
flex: 1, |
||||||
|
padding: 24, |
||||||
|
justifyContent: 'center', |
||||||
|
backgroundColor: 'grey', |
||||||
|
}, |
||||||
|
|
||||||
|
contentContainer: { |
||||||
|
flex: 1, |
||||||
|
alignItems: 'center', |
||||||
|
}, |
||||||
|
buttonContainer: { |
||||||
|
flexDirection: 'row', |
||||||
|
justifyContent: 'space-between', |
||||||
|
padding: 10, |
||||||
|
paddingBottom: 20, |
||||||
|
}, |
||||||
|
button: { |
||||||
|
flex: 1, |
||||||
|
margin: 5, |
||||||
|
borderRadius: 5 |
||||||
|
}, |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
|
||||||
|
Text: { |
||||||
|
fontSize: 16, |
||||||
|
fontWeight: 'bold', |
||||||
|
color: colors.blue |
||||||
|
}, |
||||||
|
card: { |
||||||
|
flex: 1, |
||||||
|
marginHorizontal: 8, |
||||||
|
marginVertical: 5, |
||||||
|
backgroundColor: colors.pureWhite, |
||||||
|
}, |
||||||
|
subText: { |
||||||
|
fontSize: 12, |
||||||
|
color: colors.blue |
||||||
|
}, |
||||||
|
|
||||||
|
row: { |
||||||
|
flexDirection: 'row', |
||||||
|
justifyContent: 'space-between', |
||||||
|
marginBottom: 5 |
||||||
|
}, |
||||||
|
cardContent: { |
||||||
|
flexDirection: 'row', |
||||||
|
justifyContent: 'space-between', |
||||||
|
paddingVertical: 10, |
||||||
|
}, |
||||||
|
leftContent: { |
||||||
|
flex: 1, |
||||||
|
justifyContent: 'center', |
||||||
|
alignItems: 'flex-start', |
||||||
|
marginLeft: 15 |
||||||
|
}, |
||||||
|
midContent: { |
||||||
|
flex: 7, |
||||||
|
justifyContent: 'flex-start', |
||||||
|
alignItems: 'flex-start', |
||||||
|
}, |
||||||
|
rightContent: { |
||||||
|
flex: 1, |
||||||
|
justifyContent: 'center', |
||||||
|
alignItems: 'flex-end', |
||||||
|
marginRight: 15, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,159 @@ |
|||||||
|
import React, { useState, useRef, useMemo, useCallback } from 'react'; |
||||||
|
import { TouchableRipple, TextInput, Appbar, List, Button } from 'react-native-paper'; |
||||||
|
import { View, StyleSheet, ScrollView } from 'react-native'; |
||||||
|
import DateTimePicker from '@react-native-community/datetimepicker'; |
||||||
|
import moment from 'moment'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
import { strings } from '../../../utils/i18n'; |
||||||
|
import { |
||||||
|
BottomSheetModal, |
||||||
|
BottomSheetModalProvider, |
||||||
|
BottomSheetView |
||||||
|
} from '@gorhom/bottom-sheet'; |
||||||
|
|
||||||
|
export default function ReportScreen({ route, navigation }) { |
||||||
|
const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false); |
||||||
|
const [date, setDate] = useState(''); |
||||||
|
const [status, setStatus] = useState('') |
||||||
|
const bottomSheetModal = useRef(null); |
||||||
|
const showIncidentDatePickerFunc = () => { |
||||||
|
setShowIncidentDatePicker(true); |
||||||
|
}; |
||||||
|
// const snapPoints = useMemo(() => ['10%', ], []);
|
||||||
|
const handleOpenSheet = useCallback(() => { |
||||||
|
bottomSheetModal.current?.present(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
const handleStatus = (data) => { |
||||||
|
setStatus(data) |
||||||
|
bottomSheetModal.current?.dismiss(); |
||||||
|
} |
||||||
|
|
||||||
|
const handleIncidentDateChange = (event, selectedDate) => { |
||||||
|
const currentDate = selectedDate; |
||||||
|
setShowIncidentDatePicker(false); |
||||||
|
setDate(currentDate); |
||||||
|
}; |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}> |
||||||
|
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} /> |
||||||
|
<Appbar.Content color={colors.pureWhite} title='Rencana pencegahan' /> |
||||||
|
</Appbar.Header> |
||||||
|
<View style={styles.container}> |
||||||
|
<ScrollView> |
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
underlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
label="RENCANA TINDAKAN PENCEGAHAN" |
||||||
|
mode="outlined" |
||||||
|
multiline={true} |
||||||
|
numberOfLines={5} |
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10, marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Who' |
||||||
|
placeholder='Who' |
||||||
|
/> |
||||||
|
<TouchableRipple onPress={showIncidentDatePickerFunc}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label=" When" |
||||||
|
value={date ? moment(date).format("DD-MM-YYYY") : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="calendar" onPress={showIncidentDatePickerFunc} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
<TouchableRipple onPress={handleOpenSheet}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label=" Status" |
||||||
|
value={status ? status : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="list-status" onPress={handleOpenSheet} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
</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.beanRed }]} onPress={() => console.log('Handle Saved')}> |
||||||
|
Simpan |
||||||
|
</Button> |
||||||
|
</View> |
||||||
|
|
||||||
|
{ |
||||||
|
showIncidentDatePicker && ( |
||||||
|
<DateTimePicker |
||||||
|
value={date || new Date()} |
||||||
|
mode="date" |
||||||
|
display="default" |
||||||
|
onChange={handleIncidentDateChange} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
<BottomSheetModalProvider> |
||||||
|
<BottomSheetModal |
||||||
|
ref={bottomSheetModal} |
||||||
|
index={0} |
||||||
|
snapPoints={['20%']} |
||||||
|
bottomInset={10} |
||||||
|
detached={true} |
||||||
|
style={{ marginHorizontal: 15 }} |
||||||
|
> |
||||||
|
<BottomSheetView > |
||||||
|
<TouchableRipple onPress={() => handleStatus("Open")}> |
||||||
|
<List.Item |
||||||
|
title="OPEN" |
||||||
|
left={props => <List.Icon {...props} icon="" />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
<TouchableRipple onPress={() => handleStatus("Close")}> |
||||||
|
<List.Item |
||||||
|
title="CLOSE" |
||||||
|
left={props => <List.Icon {...props} icon="" />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
</BottomSheetView> |
||||||
|
</BottomSheetModal> |
||||||
|
</BottomSheetModalProvider> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
button: { |
||||||
|
flex: 1, |
||||||
|
margin: 5, |
||||||
|
borderRadius: 5 |
||||||
|
}, |
||||||
|
buttonContainer: { |
||||||
|
flexDirection: 'row', |
||||||
|
justifyContent: 'space-between', |
||||||
|
padding: 10, |
||||||
|
paddingBottom: 20, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,75 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { TouchableRipple, Chip, Card, Text } from 'react-native-paper'; |
||||||
|
import { View, StyleSheet, ScrollView } from 'react-native'; |
||||||
|
import DateTimePicker from '@react-native-community/datetimepicker'; |
||||||
|
import moment from 'moment'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
import { jenisKejadian } from '../../../config/ApiConst' |
||||||
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons' |
||||||
|
|
||||||
|
export default function IncidentScreen() { |
||||||
|
const [selectedItems, setSelectedItems] = useState([]); |
||||||
|
const [dataIncidentReport, setDataIncidentReport] = useState(jenisKejadian[0].data) |
||||||
|
const [typeRiskIncident, setTypeRiskIncident] = useState("") |
||||||
|
|
||||||
|
const handleCheckboxChange = (item) => { |
||||||
|
const updatedDataIncidentReport = dataIncidentReport.map(dataItem => { |
||||||
|
if (dataItem.label === item.label) { |
||||||
|
return { ...dataItem, checked: !dataItem.checked }; |
||||||
|
} |
||||||
|
return dataItem; |
||||||
|
}); |
||||||
|
setDataIncidentReport(updatedDataIncidentReport); |
||||||
|
const hasCritical = updatedDataIncidentReport.some(item => item.level === 'Critical' && item.checked); |
||||||
|
setTypeRiskIncident(updatedDataIncidentReport.some(item => item.checked) ? (hasCritical ? 'Critical' : 'Major') : ''); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
const isItemSelected = (label) => { |
||||||
|
return dataIncidentReport.some(item => item.label === label && item.checked); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> |
||||||
|
Jenis kejadian |
||||||
|
</Text> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> |
||||||
|
Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
<View style={styles.container}> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16 }}> |
||||||
|
Pilih Jenis Kejadian |
||||||
|
</Text> |
||||||
|
<ScrollView style={styles.listData}> |
||||||
|
<View> |
||||||
|
{dataIncidentReport.map(item => ( |
||||||
|
<> |
||||||
|
<Chip |
||||||
|
icon={() => ( |
||||||
|
<Icon name={isItemSelected(item.label) ? 'circle-slice-8' : 'circle-outline'} size={20} color={isItemSelected(item.label) ? colors.pureWhite : colors.blue} /> |
||||||
|
)} textStyle={{ color: isItemSelected(item.label) ? colors.pureWhite : colors.blue }} onPress={() => handleCheckboxChange(item)} style={{ backgroundColor: isItemSelected(item.label) ? colors.beanRed : colors.semiBlue, marginBottom: 5 }}>{item.label}</Chip > |
||||||
|
</> |
||||||
|
))} |
||||||
|
</View> |
||||||
|
</ScrollView > |
||||||
|
</View > |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
listData: { |
||||||
|
flex: 1, |
||||||
|
marginTop: 10, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,187 @@ |
|||||||
|
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; |
||||||
|
import { TextInput, Card, Checkbox, Text, TouchableRipple } from 'react-native-paper'; |
||||||
|
import { StyleSheet, View, ScrollView } from 'react-native'; |
||||||
|
import { colors } from '../../../utils/color' |
||||||
|
import { useNavigation } from '@react-navigation/native'; |
||||||
|
|
||||||
|
export default function LocationScreen() { |
||||||
|
const navigation = useNavigation(); |
||||||
|
const [selectedProject, setSelectedProject] = useState(null); |
||||||
|
const [area, setArea] = useState({ external: false, internal: false }) |
||||||
|
const handleProjectPress = () => { |
||||||
|
navigation.navigate('SearchPage', { dummyData, onSelect: handleProjectSelect }); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleCheckboxArea = (data) => { |
||||||
|
const { name } = data; |
||||||
|
setArea(prevState => ({ |
||||||
|
...prevState, |
||||||
|
[name]: !prevState[name] |
||||||
|
})); |
||||||
|
setBottomSheetIndex(0); |
||||||
|
}; |
||||||
|
console.log("Area :", area); |
||||||
|
const handleProjectSelect = (project) => { |
||||||
|
setSelectedProject(project); |
||||||
|
}; |
||||||
|
|
||||||
|
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' } |
||||||
|
]; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> |
||||||
|
Lokasi kejadian |
||||||
|
</Text> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> |
||||||
|
Harap memasukan lokasi kejadian yang sesuai |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
<ScrollView> |
||||||
|
<View style={styles.container}> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6 }}> |
||||||
|
Lokasi Kejadian |
||||||
|
</Text> |
||||||
|
<TouchableRipple onPress={handleProjectPress}> |
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
editable={false} |
||||||
|
label='Pilih Proyek' |
||||||
|
placeholder='Pilih Proyek' |
||||||
|
value={selectedProject ? selectedProject.name : ''} |
||||||
|
right={<TextInput.Icon icon="chevron-down" />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Lokasi Kejadian' |
||||||
|
placeholder='Lokasi Kejadian' |
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Sub Lokasi Kejadian' |
||||||
|
placeholder='Sub Lokasi Kejadian' |
||||||
|
|
||||||
|
/> |
||||||
|
<View style={styles.row}> |
||||||
|
<Checkbox |
||||||
|
status={area.internal ? 'checked' : 'unchecked'} |
||||||
|
color={colors.blue} |
||||||
|
uncheckedColor={colors.amethystSmoke} |
||||||
|
onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })} |
||||||
|
/> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> |
||||||
|
Internal (Didalam area/kawasan/parimeter Project) |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
<View style={styles.row}> |
||||||
|
<Checkbox |
||||||
|
status={area.external ? 'checked' : 'unchecked'} |
||||||
|
color={colors.blue} |
||||||
|
uncheckedColor={colors.amethystSmoke} |
||||||
|
onPress={() => handleCheckboxArea({ name: 'external', checked: !area.external })} |
||||||
|
/> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> |
||||||
|
External (Diluar area/kawasan/parimeter Project) |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
|
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}> |
||||||
|
Lokasi Pelaporan |
||||||
|
</Text> |
||||||
|
<TouchableRipple onPress={handleProjectPress}> |
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Pilih Proyek' |
||||||
|
placeholder='Pilih Proyek' |
||||||
|
|
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Lokasi Laporan' |
||||||
|
placeholder='Lokasi Laporan' |
||||||
|
|
||||||
|
|
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Sub Lokasi Laporan' |
||||||
|
placeholder='Sub Lokasi Laporan' |
||||||
|
/> |
||||||
|
</View> |
||||||
|
</ScrollView> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
row: { |
||||||
|
flexDirection: 'row', |
||||||
|
alignItems: 'center', |
||||||
|
marginBottom: 5, |
||||||
|
marginRight: 15, |
||||||
|
marginLeft: 5 |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,288 @@ |
|||||||
|
import React, { useRef, useCallback, useState, useMemo } from 'react'; |
||||||
|
import { launchCamera, launchImageLibrary } from 'react-native-image-picker'; |
||||||
|
import { |
||||||
|
BottomSheetModal, |
||||||
|
BottomSheetModalProvider, |
||||||
|
BottomSheetView |
||||||
|
} from '@gorhom/bottom-sheet'; |
||||||
|
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker'; |
||||||
|
import { TextInput, IconButton, Button } from 'react-native-paper'; |
||||||
|
import { StyleSheet, View, ScrollView, PermissionsAndroid, Image } from 'react-native'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
import { strings } from '../../../utils/i18n'; |
||||||
|
import { requestAccessStoragePermission } from '../../../utils/storage'; |
||||||
|
import { getCoords } from '../../../utils/geolocation'; |
||||||
|
|
||||||
|
export default function MediaScreen() { |
||||||
|
const [images, setImages] = useState([]); |
||||||
|
|
||||||
|
const bottomSheetModal = 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 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); |
||||||
|
} |
||||||
|
bottomSheetModal.current?.dismiss(); |
||||||
|
}; |
||||||
|
|
||||||
|
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}/presensi/${imageObject.drop_point_id}_${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 handleLaunchGallery = () => { |
||||||
|
let options = { |
||||||
|
mediaType: 'photo', |
||||||
|
includeBase64: true |
||||||
|
}; |
||||||
|
|
||||||
|
launchImageLibrary(options, (response) => { |
||||||
|
if (response.didCancel) { |
||||||
|
} else if (response.error) { |
||||||
|
} else if (response.customButton) { |
||||||
|
} else { |
||||||
|
handleSetImageUri(response.assets[0].uri); |
||||||
|
} |
||||||
|
}); |
||||||
|
bottomSheetModal.current?.dismiss(); |
||||||
|
} |
||||||
|
|
||||||
|
const renderImages = useMemo(() => images.map((image, index) => ( |
||||||
|
<View key={index} style={styles.imageBlock}> |
||||||
|
<View style={styles.imageContainer}> |
||||||
|
<Image |
||||||
|
source={{ uri: image.uri }} |
||||||
|
style={styles.image} |
||||||
|
resizeMode="contain" |
||||||
|
/> |
||||||
|
<IconButton |
||||||
|
iconColor={colors.beanRed} |
||||||
|
icon="delete" |
||||||
|
style={styles.deleteButton} |
||||||
|
size={30} |
||||||
|
onPress={() => handleDeleteImage(index)} |
||||||
|
/> |
||||||
|
</View> |
||||||
|
<TextInput |
||||||
|
mode='outlined' |
||||||
|
style={styles.descriptionInput} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
placeholder="Deskripsi gambar" |
||||||
|
label="Deskripsi gambar" |
||||||
|
multiline={true} |
||||||
|
value={image.description} |
||||||
|
onChangeText={(text) => { |
||||||
|
const updatedImages = [...images]; |
||||||
|
updatedImages[index].description = text; |
||||||
|
setImages(updatedImages); |
||||||
|
}} |
||||||
|
/> |
||||||
|
</View> |
||||||
|
)), [images]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Button |
||||||
|
icon="plus" |
||||||
|
style={styles.addButton} |
||||||
|
mode="contained-tonal" |
||||||
|
onPress={handleOpenSheet} |
||||||
|
textColor={colors.blue} |
||||||
|
> |
||||||
|
Tambah Gambar |
||||||
|
</Button> |
||||||
|
|
||||||
|
<ScrollView> |
||||||
|
<View style={styles.container}> |
||||||
|
{renderImages} |
||||||
|
</View> |
||||||
|
</ScrollView> |
||||||
|
<BottomSheetModalProvider> |
||||||
|
<BottomSheetModal |
||||||
|
ref={bottomSheetModal} |
||||||
|
index={0} |
||||||
|
snapPoints={['30%']} |
||||||
|
bottomInset={10} |
||||||
|
detached={true} |
||||||
|
style={styles.bottomSheet} |
||||||
|
> |
||||||
|
<BottomSheetView> |
||||||
|
<View style={styles.sheetButtonContainer}> |
||||||
|
<IconButton |
||||||
|
icon="camera" |
||||||
|
size={50} |
||||||
|
onPress={handleTakePicture} |
||||||
|
/> |
||||||
|
<IconButton |
||||||
|
icon="image" |
||||||
|
size={50} |
||||||
|
onPress={handleLaunchGallery} |
||||||
|
/> |
||||||
|
</View> |
||||||
|
</BottomSheetView> |
||||||
|
</BottomSheetModal> |
||||||
|
</BottomSheetModalProvider> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10, |
||||||
|
}, |
||||||
|
imageBlock: { |
||||||
|
marginBottom: 20, |
||||||
|
flexDirection: 'row', |
||||||
|
}, |
||||||
|
imageContainer: { |
||||||
|
flexDirection: 'column', |
||||||
|
alignItems: 'center', |
||||||
|
marginHorizontal: 5 |
||||||
|
}, |
||||||
|
image: { |
||||||
|
width: 100, |
||||||
|
height: 100, |
||||||
|
borderRadius: 7 |
||||||
|
}, |
||||||
|
descriptionInput: { |
||||||
|
flex: 1, |
||||||
|
marginRight: 10, |
||||||
|
}, |
||||||
|
addButton: { |
||||||
|
marginHorizontal: 8, |
||||||
|
marginVertical: 10, |
||||||
|
borderRadius: 10, |
||||||
|
backgroundColor: colors.semiBlue, |
||||||
|
}, |
||||||
|
bottomSheet: { |
||||||
|
marginHorizontal: 15, |
||||||
|
}, |
||||||
|
sheetButtonContainer: { |
||||||
|
flexDirection: 'row', |
||||||
|
justifyContent: 'space-around', |
||||||
|
padding: 10, |
||||||
|
paddingBottom: 20, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,103 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { TouchableRipple, TextInput, Card, Text } from 'react-native-paper'; |
||||||
|
import { View, StyleSheet, ScrollView } from 'react-native'; |
||||||
|
import DateTimePicker from '@react-native-community/datetimepicker'; |
||||||
|
import moment from 'moment'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
|
||||||
|
export default function ReportScreen() { |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> |
||||||
|
Pelapor dan penerima laporan |
||||||
|
</Text> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> |
||||||
|
Siapa yang membuat laporan dan siapa yang membuat laporan |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
<ScrollView> |
||||||
|
<View style={styles.container}> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6 }}> |
||||||
|
Pelapor |
||||||
|
</Text> |
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label=" Nama Pelapor" |
||||||
|
placeholder='Nama' |
||||||
|
|
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Posisi/Jabatan' |
||||||
|
placeholder='Posisi/Jabatan' |
||||||
|
|
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='No Contact' |
||||||
|
placeholder='No Contact' |
||||||
|
/> |
||||||
|
|
||||||
|
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}> |
||||||
|
Penerima Laporan |
||||||
|
</Text> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Nama' |
||||||
|
placeholder='Nama' |
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='Posisi/Jabatan' |
||||||
|
placeholder='Posisi/Jabatan' |
||||||
|
/> |
||||||
|
|
||||||
|
<TextInput |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label='No Contact' |
||||||
|
placeholder='No Contact' |
||||||
|
/> |
||||||
|
</View> |
||||||
|
</ScrollView> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,168 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { TouchableRipple, TextInput, Card, Text } from 'react-native-paper'; |
||||||
|
import { View, StyleSheet } from 'react-native'; |
||||||
|
import DateTimePicker from '@react-native-community/datetimepicker'; |
||||||
|
import moment from 'moment'; |
||||||
|
import { colors } from '../../../utils/color'; |
||||||
|
|
||||||
|
export default function TimeScreen() { |
||||||
|
const [incidentDate, setIncidentDate] = useState(''); |
||||||
|
const [incidentTime, setIncidentTime] = useState(''); |
||||||
|
const [reportDate, setReportDate] = useState(''); |
||||||
|
const [reportTime, setReportTime] = useState(''); |
||||||
|
const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false); |
||||||
|
const [showIncidentTimePicker, setShowIncidentTimePicker] = useState(false); |
||||||
|
const [showReportDatePicker, setShowReportDatePicker] = useState(false); |
||||||
|
const [showReportTimePicker, setShowReportTimePicker] = useState(false); |
||||||
|
|
||||||
|
const handleIncidentDateChange = (event, selectedDate) => { |
||||||
|
const currentDate = selectedDate; |
||||||
|
setShowIncidentDatePicker(false); |
||||||
|
setIncidentDate(currentDate); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleIncidentTimeChange = (event, selectedDate) => { |
||||||
|
const currentDate = selectedDate; |
||||||
|
setShowIncidentTimePicker(false); |
||||||
|
setIncidentTime(currentDate); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleReportDateChange = (event, selectedDate) => { |
||||||
|
const currentDate = selectedDate; |
||||||
|
setShowReportDatePicker(false); |
||||||
|
setReportDate(currentDate); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleReportTimeChange = (event, selectedDate) => { |
||||||
|
const currentDate = selectedDate; |
||||||
|
setShowReportTimePicker(false); |
||||||
|
setReportTime(currentDate); |
||||||
|
}; |
||||||
|
|
||||||
|
const showIncidentDatePickerFunc = () => { |
||||||
|
setShowIncidentDatePicker(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const showIncidentTimePickerFunc = () => { |
||||||
|
setShowIncidentTimePicker(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const showReportDatePickerFunc = () => { |
||||||
|
setShowReportDatePicker(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const showReportTimePickerFunc = () => { |
||||||
|
setShowReportTimePicker(true); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
|
||||||
|
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> |
||||||
|
<View> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> |
||||||
|
Laporan Awal Insiden |
||||||
|
</Text> |
||||||
|
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> |
||||||
|
Wajib melaporkan maksimal 1x3 jam setelah kejadian |
||||||
|
</Text> |
||||||
|
</View> |
||||||
|
</Card> |
||||||
|
<View style={[styles.container, { backgroundColor: 'pureWhite' }]}> |
||||||
|
<TouchableRipple onPress={showIncidentDatePickerFunc}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label=" Tanggal Kejadian" |
||||||
|
value={incidentDate ? moment(incidentDate).format("DD-MM-YYYY") : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="calendar" onPress={showIncidentDatePickerFunc} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
<TouchableRipple onPress={showIncidentTimePickerFunc}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label="Waktu Kejadian" |
||||||
|
value={incidentTime ? moment(incidentTime).format("HH:m") : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="clock-time-eight-outline" onPress={showIncidentTimePickerFunc} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
|
||||||
|
<TouchableRipple onPress={showReportDatePickerFunc}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label="Tanggal Laporan" |
||||||
|
value={reportDate ? moment(reportDate).format("DD-MM-YYYY") : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="calendar" onPress={showReportDatePickerFunc} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
<TouchableRipple onPress={showReportTimePickerFunc}> |
||||||
|
<TextInput |
||||||
|
style={{ marginBottom: 15 }} |
||||||
|
dense={true} |
||||||
|
outlineColor={colors.amethystSmoke} |
||||||
|
activeOutlineColor={colors.blue} |
||||||
|
mode="outlined" |
||||||
|
label=" Waktu Laporan" |
||||||
|
value={reportTime ? moment(reportTime).format("HH:m") : ''} |
||||||
|
editable={false} |
||||||
|
right={<TextInput.Icon icon="clock-time-eight-outline" onPress={showReportTimePickerFunc} />} |
||||||
|
/> |
||||||
|
</TouchableRipple> |
||||||
|
</View> |
||||||
|
{showIncidentDatePicker && ( |
||||||
|
<DateTimePicker |
||||||
|
value={incidentDate || new Date()} |
||||||
|
mode="date" |
||||||
|
display="default" |
||||||
|
onChange={handleIncidentDateChange} |
||||||
|
/> |
||||||
|
)} |
||||||
|
{showIncidentTimePicker && ( |
||||||
|
<DateTimePicker |
||||||
|
value={incidentTime || new Date()} |
||||||
|
mode="time" |
||||||
|
display="default" |
||||||
|
onChange={handleIncidentTimeChange} |
||||||
|
/> |
||||||
|
)} |
||||||
|
{showReportDatePicker && ( |
||||||
|
<DateTimePicker |
||||||
|
value={reportDate || new Date()} |
||||||
|
mode="date" |
||||||
|
display="default" |
||||||
|
onChange={handleReportDateChange} |
||||||
|
/> |
||||||
|
)} |
||||||
|
{showReportTimePicker && ( |
||||||
|
<DateTimePicker |
||||||
|
value={reportTime || new Date()} |
||||||
|
mode="time" |
||||||
|
display="default" |
||||||
|
onChange={handleReportTimeChange} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const styles = StyleSheet.create({ |
||||||
|
container: { |
||||||
|
flex: 1, |
||||||
|
marginVertical: 10, |
||||||
|
marginHorizontal: 10 |
||||||
|
}, |
||||||
|
}); |
Loading…
Reference in new issue