Browse Source

feat(module):incindent module

master
farhantock 8 months ago
parent
commit
5891cc8da3
  1. 41
      src/config/ApiConst.js
  2. 17
      src/screens/Login.js
  3. 145
      src/screens/incident-page/dialogFrom.js
  4. 109
      src/screens/incident-page/index.js
  5. 361
      src/screens/incident-page/stepComponent/chronology.js
  6. 159
      src/screens/incident-page/stepComponent/containedAction.js
  7. 75
      src/screens/incident-page/stepComponent/incident.js
  8. 187
      src/screens/incident-page/stepComponent/location.js
  9. 288
      src/screens/incident-page/stepComponent/media.js
  10. 103
      src/screens/incident-page/stepComponent/report.js
  11. 168
      src/screens/incident-page/stepComponent/time.js

41
src/config/ApiConst.js

@ -9,3 +9,44 @@ export const BASE_URL_IMAGE = `${BASE_URL_API_V1}/attachments`
export const LOGIN_STATE_CHANGED = 'LOGIN_STATE_CHANGED'; export const LOGIN_STATE_CHANGED = 'LOGIN_STATE_CHANGED';
export const ERROR_LOGIN = "ERROR_LOGIN" export const ERROR_LOGIN = "ERROR_LOGIN"
export const jenisKejadian = [
{
"data": [
{ "label": "Vandalisme aset perusahaan", "level": "Critical", "checked": false },
{ "label": "Perusakan aset", "level": "Critical", "checked": false },
{ "label": "Pencurian", "level": "Critical", "checked": false },
{ "label": "Penerobosan", "level": "Critical", "checked": false },
{ "label": "Kehilangan", "level": "Critical", "checked": false },
{ "label": "Peledakan", "level": "Critical", "checked": false },
{ "label": "Pembakaran", "level": "Critical", "checked": false },
{ "label": "Pencemaran lingkungan", "level": "Critical", "checked": false },
{ "label": "Perampokan", "level": "Critical", "checked": false },
{ "label": "Pemalakan / Pemerasan", "level": "Critical", "checked": false },
{ "label": "Kerusuhan sosial", "level": "Critical", "checked": false },
{ "label": "Pemogokan kerja", "level": "Critical", "checked": false },
{ "label": "Keluhan User", "level": "Critical", "checked": false },
{ "label": "Serangan kekerasan", "level": "Critical", "checked": false },
{ "label": "Gangguan masyarakat", "level": "Critical", "checked": false },
{ "label": "Sabotase", "level": "Critical", "checked": false },
{ "label": "Narkoba", "level": "Critical", "checked": false },
{ "label": "Minuman berakohol", "level": "Critical", "checked": false },
{ "label": "Perselisihan ketenagakerjaan", "level": "Critical", "checked": false },
{ "label": "Tidak ada tindakan/respond", "level": "Critical", "checked": false },
{ "label": "Pelanggaran SOP", "level": "Major", "checked": false },
{ "label": "Indisipliner kerja anggota", "level": "Major", "checked": false },
{ "label": "Kelengkapan seragam", "level": "Major", "checked": false },
{ "label": "Kecelakaan kerja", "level": "Major", "checked": false },
{ "label": "Kecelakaan Lalu Lintas", "level": "Major", "checked": false },
{ "label": "Kerusakan Kendaraan Project", "level": "Major", "checked": false },
{ "label": "Kerusakan Equipment Project", "level": "Major", "checked": false },
{ "label": "Keterlambatan Pembayaran Vendor", "level": "Major", "checked": false },
{ "label": "Keterlambatan Penggajian", "level": "Major", "checked": false },
{ "label": "Lain-lain", "level": "Major", "checked": false }
]
}
]

17
src/screens/Login.js

@ -5,12 +5,11 @@ import { Button, TextInput, useTheme, Text, IconButton } from 'react-native-pape
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { setIsLogin, setUser } from '../appredux/actions'; import { setIsLogin, setUser } from '../appredux/actions';
import { store } from '../appredux/store'; import { store } from '../appredux/store';
import { colors } from '../utils/color';
import LOGIN_BANNER from '../assets/images/logo_nawakara.png'; import LOGIN_BANNER from '../assets/images/logo_nawakara.png';
import { strings } from '../utils/i18n'; import { strings } from '../utils/i18n';
import { initFirebase } from '../utils/Auth'; import { initFirebase } from '../utils/Auth';
const LoginScreen = ({ route, navigation }) => { const LoginScreen = ({ route, navigation }) => {
const theme = useTheme();
const { scannedCode } = useSelector(state => state.userReducer) const { scannedCode } = useSelector(state => state.userReducer)
const [loginProcess, setLoginProcess] = useState(false); const [loginProcess, setLoginProcess] = useState(false);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
@ -32,7 +31,7 @@ const LoginScreen = ({ route, navigation }) => {
return ( return (
<> <>
<StatusBar backgroundColor="#00042A" barStyle='light-content' translucent={true} /> <StatusBar backgroundColor={colors.white} barStyle='dark-content' translucent={true} />
<KeyboardAvoidingView ehavior={Platform.OS === "ios" ? "padding" : null} style={{ flex: 1 }}> <KeyboardAvoidingView ehavior={Platform.OS === "ios" ? "padding" : null} style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}> <ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={styles.container}> <View style={styles.container}>
@ -43,8 +42,8 @@ const LoginScreen = ({ route, navigation }) => {
resizeMode="contain" resizeMode="contain"
/> />
<Text variant="displaySmall" >{strings('loginPage.headMessage')}</Text> <Text variant="displaySmall" style={{ color: colors.black, fontFamily: 'Roboto-Bold' }}>{strings('loginPage.headMessage')}</Text>
<Text variant="bodyMedium" >Silahkan isi Username dan kata sandi anda</Text> <Text variant="bodyMedium" style={{ color: colors.mistBlue }}>Silahkan isi Username dan kata sandi anda</Text>
<Controller <Controller
control={control} control={control}
rules={{ rules={{
@ -53,6 +52,8 @@ const LoginScreen = ({ route, navigation }) => {
}} }}
render={({ field: { onChange, onBlur, value } }) => ( render={({ field: { onChange, onBlur, value } }) => (
<TextInput <TextInput
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label="Username" label="Username"
onBlur={onBlur} onBlur={onBlur}
@ -71,6 +72,8 @@ const LoginScreen = ({ route, navigation }) => {
}} }}
render={({ field: { onChange, onBlur, value } }) => ( render={({ field: { onChange, onBlur, value } }) => (
<TextInput <TextInput
underlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label="Password" label="Password"
onBlur={onBlur} onBlur={onBlur}
@ -93,9 +96,9 @@ const LoginScreen = ({ route, navigation }) => {
<Button <Button
mode="contained" mode="contained"
onPress={handleSubmit(onSubmitLogin)} onPress={handleSubmit(onSubmitLogin)}
style={{ marginTop: 15, backgroundColor: '#1C1B40' }} style={{ marginTop: 15, backgroundColor: colors.blue, borderRadius: 10 }}
disabled={!isDirty || !isValid || isSubmitting} disabled={!isDirty || !isValid || isSubmitting}
labelStyle={{ color: theme.colors.surface }} labelStyle={{ color: colors.white }}
> >
{isSubmitting ? "Loading..." : strings('loginPage.signInBtn')} {isSubmitting ? "Loading..." : strings('loginPage.signInBtn')}
</Button> </Button>

145
src/screens/incident-page/dialogFrom.js

@ -1,37 +1,83 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { Icon, Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper'; import { StatusBar } from 'react-native';
import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions } from 'react-native'; import { Button, Text, Appbar, } from 'react-native-paper';
import { StyleSheet, View, } from 'react-native';
import { colors } from '../../utils/color';
import { strings } from '../../utils/i18n';
import StepIndicator from 'react-native-step-indicator';
import TimeComponent from './stepComponent/time'
import LocationScreen from './stepComponent/location';
import ReportScreen from './stepComponent/report';
import IncidentScreen from './stepComponent/incident';
import ChronologyScreen from './stepComponent/chronology';
import MediaScreen from './stepComponent/media';
export default function DialogForm({ route, navigation }) { export default function DialogForm({ route, navigation }) {
const [active, setActive] = useState(0);
const labels = ["Waktu", "Lokasi", "Pelapor", "Kejadian", "Kronologis", "Media"];
const renderStepContent = (step) => {
switch (step) {
case 0:
return <TimeComponent />;
case 1:
return <LocationScreen />;
case 2:
return <ReportScreen />
case 3:
return <IncidentScreen />;
case 4:
return <ChronologyScreen navigation={navigation} />;
case 5:
return <MediaScreen />;
}
};
const handleBack = () => {
if (active > 0) {
setActive(active - 1);
}
};
const handleNext = () => {
if (active < labels.length - 1) {
setActive(active + 1);
}
};
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Appbar.Header mode='center-aligned' style={{ backgroundColor: 'white' }}> <StatusBar backgroundColor={colors.beanRed} barStyle="light-content" />
<Appbar.BackAction onPress={() => { navigation.goBack() }} /> <Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}>
<Appbar.Content title="Form Insiden" /> <Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} />
<Appbar.Content color={colors.pureWhite} title={strings('incidentReport.title')} />
</Appbar.Header> </Appbar.Header>
<View style={styles.cardView}> <View style={{ width: '100%', marginTop: 15, marginBottom: 2, backgroundColor: colors.pureWhite }}>
<Card style={styles.card}> <StepIndicator
<Card.Title title="Pencurian" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> stepCount={labels.length}
customStyles={stepIndicatorStyles}
</Card> currentPosition={active}
<Card style={styles.card}> labels={labels}
<Card.Title title="Sabotase" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> onPress={(position) => setActive(position)}
/>
</Card> </View>
<Card style={styles.card}> <View style={styles.contentContainer}>
<Card.Title title="Gangguan Masyarakat" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> {renderStepContent(active)}
</Card> </View>
<Card style={styles.card}> <View style={styles.buttonContainer}>
<Card.Title title="Unjuk Rasa" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> <Button mode="outlined" style={styles.button} textColor={colors.mistBlue} onPress={handleBack} disabled={active === 0}>
</Card> Kembali
<Card style={styles.card}> </Button>
<Card.Title title="Kehilangan Barang" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> {active === labels.length - 1 ?
</Card> (
<Card style={styles.card}> <Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}>
<Card.Title title="Penerobosan" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> Simpan
</Card> </Button>
<Card style={styles.card}> )
<Card.Title title="Terorisme" titleVariant='labelLarge' titleStyle={{ fontWeight: 'bold', color: 'white' }} rightStyle={{ marginRight: 20 }} right={() => <Icon source="plus-circle-outline" size={24} color='white' />} /> : (
</Card> <Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={handleNext}>
Lanjut
</Button>
)}
</View> </View>
</View > </View >
) )
@ -41,6 +87,7 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 20, marginTop: 20,
backgroundColor: colors.pureWhite
}, },
cardView: { cardView: {
marginTop: 10, marginTop: 10,
@ -50,5 +97,45 @@ const styles = StyleSheet.create({
marginHorizontal: 10, marginHorizontal: 10,
backgroundColor: '#1C1B40', backgroundColor: '#1C1B40',
}, },
button: {
flex: 1,
margin: 5,
borderRadius: 5
},
contentContainer: {
flex: 1,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
paddingBottom: 20,
},
}) })
const stepIndicatorStyles = {
stepIndicatorSize: 25,
currentStepIndicatorSize: 30,
separatorStrokeWidth: 2,
currentStepStrokeWidth: 2,
stepIndicatorCurrentColor: colors.beanRed,
stepStrokeCurrentColor: colors.beanRed,
stepStrokeWidth: 1,
stepStrokeFinishedColor: colors.beanRed,
stepStrokeUnFinishedColor: colors.semiRed,
separatorFinishedColor: colors.beanRed,
separatorUnFinishedColor: colors.semiRed,
stepIndicatorFinishedColor: colors.beanRed,
stepIndicatorUnFinishedColor: colors.semiRed,
stepIndicatorCurrentColor: colors.beanRed,
stepIndicatorLabelFontSize: 12,
currentStepIndicatorLabelFontSize: 12,
stepIndicatorLabelCurrentColor: colors.pureWhite,
stepIndicatorLabelFinishedColor: colors.pureWhite,
stepIndicatorLabelUnFinishedColor: colors.beanRed,
labelColor: colors.semiRed,
labelSize: 12,
currentStepLabelColor: colors.beanRed
}

109
src/screens/incident-page/index.js

@ -1,35 +1,54 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { Icon, Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper'; import { Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper';
import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions } from 'react-native'; import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions, StatusBar } from 'react-native';
import { colors } from '../../utils/color';
import Icon from 'react-native-vector-icons/AntDesign';
import { strings } from '../../utils/i18n';
export default function IncidentScreen({ route, navigation }) { export default function IncidentScreen({ route, navigation }) {
const incidentData = [
{ id: 1, date: "Senin, 02-05-2024 21:03", location: "Kangean Site Indonesia", company: "Kangean Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "KEI2301M" },
{ id: 2, date: "Selasa, 03-05-2024 10:15", location: "Sumatra Site Indonesia", company: "Sumatra Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2402M" },
{ id: 3, date: "Rabu, 04-05-2024 15:20", location: "Java Site Indonesia", company: "Java Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "JEI2503M" },
{ id: 4, date: "Kamis, 05-05-2024 18:30", location: "Bali Site Indonesia", company: "Bali Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "BEI2604M" },
{ id: 5, date: "Jumat, 06-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" },
{ id: 5, date: "Sabtu, 07-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" },
{ id: 5, date: "Minggu, 08-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" }
];
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Appbar.Header mode='center-aligned' style={{ backgroundColor: 'white' }}> <StatusBar backgroundColor={colors.white} barStyle='dark-content' translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.pureWhite, elevation: 4 }}>
<Appbar.BackAction onPress={() => { navigation.goBack() }} /> <Appbar.BackAction onPress={() => { navigation.goBack() }} />
<Appbar.Content title="Laporan Insiden" /> <Appbar.Content title={strings('incidentReport.title')} />
<Appbar.Action icon='filter-plus-outline' onPress={() => { }} />
<Appbar.Action icon='plus' onPress={() => { }} />
</Appbar.Header> </Appbar.Header>
<ScrollView>
<Card style={styles.card}>
<Card.Title title="Semen lebak factory" titleVariant='titleMedium' subtitle="20-02-2024 22:20:17" subtitleStyle={{ color: 'white' }} titleStyle={{ fontWeight: 'bold', color: 'white' }} />
<Card.Content style={styles.cardContent}>
<Text variant="titleMedium" style={{ color: 'white' }} >Jenis Insiden</Text> <View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Text variant="titleMedium" style={{ color: 'white' }} >SICN</Text> <Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginHorizontal: 8, }} textColor={colors.blue} mode="contained-tonal" onPress={() => { navigation.navigate('DialogFormIncident') }}>
</Card.Content> {strings('incidentReport.add')}
<Card.Actions style={styles.cardActions}>
<Button icon="view-list" textColor='#1C1B40' buttonColor='white' mode="contained" onPress={() => console.log('Pressed')}>
Detail
</Button>
<Button icon="view-list" textColor='#1C1B40' buttonColor='white' mode="contained" onPress={() => console.log('Pressed')}>
Ubah
</Button> </Button>
<Button icon="trash-can-outline" iconColor={'white'} buttonColor='#EF6262' mode="contained" onPress={() => console.log('Pressed')}> </View>
Hapus
</Button> <ScrollView style={{ backgroundColor: colors.pureWhite }}>
</Card.Actions> {incidentData.map((incident) => (
<Card key={incident.id} elevation={2} style={styles.card}>
<View style={styles.cardContent}>
<View style={styles.leftContent}>
<Icon name="warning" size={25} color={colors.blue} />
</View>
<View style={styles.midContent}>
<Text variant="bodySmall" style={styles.subText}>{incident.date}</Text>
<Text variant="headlineMedium" style={styles.Text}>{incident.location}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.company}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.icn} - {incident.sicn}</Text>
</View>
<View style={styles.rightContent}>
<Icon name="right" size={25} color={colors.blue} />
</View>
</View>
</Card> </Card>
))}
</ScrollView> </ScrollView>
</View > </View >
) )
@ -39,22 +58,44 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 20, marginTop: 20,
backgroundColor: colors.pureWhite
},
Text: {
fontSize: 16,
fontWeight: 'bold',
color: colors.blue
}, },
card: { card: {
marginTop: 15, flex: 1,
marginHorizontal: 10, marginHorizontal: 8,
marginVertical: 2, marginVertical: 5,
backgroundColor: '#1C1B40', backgroundColor: colors.pureWhite,
}, },
cardActions: { subText: {
justifyContent: 'space-between', fontSize: 12,
backgroundColor: '#1C1B40', color: colors.blue
borderBottomRightRadius: 20,
borderBottomLeftRadius: 20,
marginVertical: 4,
}, },
cardContent: { cardContent: {
paddingHorizontal: 10, 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,
}, },
row: { row: {
flexDirection: 'row', flexDirection: 'row',

361
src/screens/incident-page/stepComponent/chronology.js

@ -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,
},
});

159
src/screens/incident-page/stepComponent/containedAction.js

@ -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,
},
});

75
src/screens/incident-page/stepComponent/incident.js

@ -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,
},
});

187
src/screens/incident-page/stepComponent/location.js

@ -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
},
});

288
src/screens/incident-page/stepComponent/media.js

@ -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,
},
});

103
src/screens/incident-page/stepComponent/report.js

@ -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
},
});

168
src/screens/incident-page/stepComponent/time.js

@ -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…
Cancel
Save