From 5891cc8da3170daa81bc4353b763f2c56bbb43dd Mon Sep 17 00:00:00 2001 From: farhantock Date: Wed, 15 May 2024 19:23:10 +0700 Subject: [PATCH] feat(module):incindent module --- src/config/ApiConst.js | 43 ++- src/screens/Login.js | 17 +- src/screens/incident-page/dialogFrom.js | 147 +++++-- src/screens/incident-page/index.js | 113 ++++-- .../incident-page/stepComponent/chronology.js | 361 ++++++++++++++++++ .../stepComponent/containedAction.js | 159 ++++++++ .../incident-page/stepComponent/incident.js | 75 ++++ .../incident-page/stepComponent/location.js | 187 +++++++++ .../incident-page/stepComponent/media.js | 288 ++++++++++++++ .../incident-page/stepComponent/report.js | 103 +++++ .../incident-page/stepComponent/time.js | 168 ++++++++ 11 files changed, 1587 insertions(+), 74 deletions(-) create mode 100644 src/screens/incident-page/stepComponent/chronology.js create mode 100644 src/screens/incident-page/stepComponent/containedAction.js create mode 100644 src/screens/incident-page/stepComponent/incident.js create mode 100644 src/screens/incident-page/stepComponent/location.js create mode 100644 src/screens/incident-page/stepComponent/media.js create mode 100644 src/screens/incident-page/stepComponent/report.js create mode 100644 src/screens/incident-page/stepComponent/time.js diff --git a/src/config/ApiConst.js b/src/config/ApiConst.js index 3d4cdb6..e425ab4 100644 --- a/src/config/ApiConst.js +++ b/src/config/ApiConst.js @@ -8,4 +8,45 @@ export const BASE_URL_IMAGE = `${BASE_URL_API_V1}/attachments` export const LOGIN_STATE_CHANGED = 'LOGIN_STATE_CHANGED'; -export const ERROR_LOGIN = "ERROR_LOGIN" \ No newline at end of file +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 } + ] + + } +] \ No newline at end of file diff --git a/src/screens/Login.js b/src/screens/Login.js index 9d081a6..571ef5c 100644 --- a/src/screens/Login.js +++ b/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 { setIsLogin, setUser } from '../appredux/actions'; import { store } from '../appredux/store'; +import { colors } from '../utils/color'; import LOGIN_BANNER from '../assets/images/logo_nawakara.png'; import { strings } from '../utils/i18n'; import { initFirebase } from '../utils/Auth'; - const LoginScreen = ({ route, navigation }) => { - const theme = useTheme(); const { scannedCode } = useSelector(state => state.userReducer) const [loginProcess, setLoginProcess] = useState(false); const [showPassword, setShowPassword] = useState(false); @@ -32,7 +31,7 @@ const LoginScreen = ({ route, navigation }) => { return ( <> - + @@ -43,8 +42,8 @@ const LoginScreen = ({ route, navigation }) => { resizeMode="contain" /> - {strings('loginPage.headMessage')} - Silahkan isi Username dan kata sandi anda + {strings('loginPage.headMessage')} + Silahkan isi Username dan kata sandi anda { }} render={({ field: { onChange, onBlur, value } }) => ( { }} render={({ field: { onChange, onBlur, value } }) => ( { diff --git a/src/screens/incident-page/dialogFrom.js b/src/screens/incident-page/dialogFrom.js index 5e698b0..c2b87c1 100644 --- a/src/screens/incident-page/dialogFrom.js +++ b/src/screens/incident-page/dialogFrom.js @@ -1,37 +1,83 @@ import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; -import { Icon, Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper'; -import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions } from 'react-native'; +import { StatusBar } 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 }) { + const [active, setActive] = useState(0); + const labels = ["Waktu", "Lokasi", "Pelapor", "Kejadian", "Kronologis", "Media"]; + + const renderStepContent = (step) => { + switch (step) { + case 0: + return ; + case 1: + return ; + case 2: + return + case 3: + return ; + case 4: + return ; + case 5: + return ; + } + }; + const handleBack = () => { + if (active > 0) { + setActive(active - 1); + } + }; + + const handleNext = () => { + if (active < labels.length - 1) { + setActive(active + 1); + } + }; return ( - - { navigation.goBack() }} /> - + + + { navigation.goBack() }} /> + - - - } /> - - - - } /> - - - - } /> - - - } /> - - - } /> - - - } /> - - - } /> - + + setActive(position)} + /> + + + {renderStepContent(active)} + + + + {active === labels.length - 1 ? + ( + + ) + : ( + + )} + ) @@ -41,6 +87,7 @@ const styles = StyleSheet.create({ container: { flex: 1, marginTop: 20, + backgroundColor: colors.pureWhite }, cardView: { marginTop: 10, @@ -50,5 +97,45 @@ const styles = StyleSheet.create({ marginHorizontal: 10, backgroundColor: '#1C1B40', }, + button: { + flex: 1, + margin: 5, + borderRadius: 5 + }, + contentContainer: { + flex: 1, + }, + buttonContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + padding: 10, + paddingBottom: 20, + }, + +}) + +const stepIndicatorStyles = { -}) \ No newline at end of file + 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 +} \ No newline at end of file diff --git a/src/screens/incident-page/index.js b/src/screens/incident-page/index.js index 15e0763..3fa7047 100644 --- a/src/screens/incident-page/index.js +++ b/src/screens/incident-page/index.js @@ -1,35 +1,54 @@ import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; -import { Icon, Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper'; -import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions } from 'react-native'; +import { Card, Text, Avatar, useTheme, IconButton, Appbar, Button } from 'react-native-paper'; +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 }) { + 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 ( - + + { navigation.goBack() }} /> - - { }} /> - { }} /> + - - - - - Jenis Insiden - SICN - - - - - - - + + + + + + {incidentData.map((incident) => ( + + + + + + + {incident.date} + {incident.location} + {incident.company} + {incident.icn} - {incident.sicn} + + + + + + + ))} ) @@ -39,22 +58,44 @@ const styles = StyleSheet.create({ container: { flex: 1, marginTop: 20, + backgroundColor: colors.pureWhite + }, + Text: { + fontSize: 16, + fontWeight: 'bold', + color: colors.blue }, card: { - marginTop: 15, - marginHorizontal: 10, - marginVertical: 2, - backgroundColor: '#1C1B40', + flex: 1, + marginHorizontal: 8, + marginVertical: 5, + backgroundColor: colors.pureWhite, }, - cardActions: { - justifyContent: 'space-between', - backgroundColor: '#1C1B40', - borderBottomRightRadius: 20, - borderBottomLeftRadius: 20, - marginVertical: 4, + subText: { + fontSize: 12, + color: colors.blue }, 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: { flexDirection: 'row', diff --git a/src/screens/incident-page/stepComponent/chronology.js b/src/screens/incident-page/stepComponent/chronology.js new file mode 100644 index 0000000..9e1cf1e --- /dev/null +++ b/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) => ( + + handleSelectImpact(data)}> + + + + ); + const renderLikelihood = (data) => ( + + handleSelectLikelihood(data)}> + + + + ); + + return ( + <> + + + + Kronologis kejadian + + + Jelaskan bagaimana terjadinya insiden + + + + + + { + setChronology(text) + }} + /> + + { + setChronology(text) + }} + /> + + } + /> + + {incidentRiskPotential.impactDescription && ( + + + {incidentRiskPotential.impactDescription} + + + )} + + } + /> + + + {incidentRiskPotential.likelihoodDescription && ( + + + {incidentRiskPotential.likelihoodDescription} + + + )} + + + + Critical + + + + + {riskLevel.level} {incidentRiskPotential.value} + + + + + Rencana tindakan pencegahan + + + + + + + + + 01-03-2024 + Farhan + Melakukan pengamatan dan patroli area pagar luar Depo MRT + + + + + + + + + {/* color: (typeRiskIncident === 'Critical' ? '#D9534F' : (typeRiskIncident === 'Major' ? '#EC9C3D' : 'black')) */} + + + + + + + {dataSelectImpact.map(item => renderImpact(item))} + + + + + + + {dataSelectLikelihood.map(item => renderLikelihood(item))} + + + + + ) +} + +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, + }, +}); diff --git a/src/screens/incident-page/stepComponent/containedAction.js b/src/screens/incident-page/stepComponent/containedAction.js new file mode 100644 index 0000000..2fb8a9a --- /dev/null +++ b/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 ( + <> + + { navigation.goBack() }} /> + + + + + + + + + } + /> + + + } + /> + + + + + + + + + + { + showIncidentDatePicker && ( + + ) + } + + + + + handleStatus("Open")}> + } + /> + + handleStatus("Close")}> + } + /> + + + + + + ) +} + +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, + }, +}); diff --git a/src/screens/incident-page/stepComponent/incident.js b/src/screens/incident-page/stepComponent/incident.js new file mode 100644 index 0000000..bb3df9c --- /dev/null +++ b/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 ( + <> + + + + Jenis kejadian + + + Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu + + + + + + Pilih Jenis Kejadian + + + + {dataIncidentReport.map(item => ( + <> + ( + + )} 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} + + ))} + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginVertical: 10, + marginHorizontal: 10 + }, + listData: { + flex: 1, + marginTop: 10, + }, +}); diff --git a/src/screens/incident-page/stepComponent/location.js b/src/screens/incident-page/stepComponent/location.js new file mode 100644 index 0000000..37222e5 --- /dev/null +++ b/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 ( + <> + + + + Lokasi kejadian + + + Harap memasukan lokasi kejadian yang sesuai + + + + + + + Lokasi Kejadian + + + } + /> + + + + + + + handleCheckboxArea({ name: 'internal', checked: !area.internal })} + /> + + Internal (Didalam area/kawasan/parimeter Project) + + + + handleCheckboxArea({ name: 'external', checked: !area.external })} + /> + + External (Diluar area/kawasan/parimeter Project) + + + + + Lokasi Pelaporan + + + + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginVertical: 10, + marginHorizontal: 10 + }, + row: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 5, + marginRight: 15, + marginLeft: 5 + }, +}); \ No newline at end of file diff --git a/src/screens/incident-page/stepComponent/media.js b/src/screens/incident-page/stepComponent/media.js new file mode 100644 index 0000000..9c5aa94 --- /dev/null +++ b/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) => ( + + + + handleDeleteImage(index)} + /> + + { + const updatedImages = [...images]; + updatedImages[index].description = text; + setImages(updatedImages); + }} + /> + + )), [images]); + + return ( + <> + + + + + {renderImages} + + + + + + + + + + + + + + ) +} + +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, + }, +}); diff --git a/src/screens/incident-page/stepComponent/report.js b/src/screens/incident-page/stepComponent/report.js new file mode 100644 index 0000000..aa7f7b6 --- /dev/null +++ b/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 ( + <> + + + + Pelapor dan penerima laporan + + + Siapa yang membuat laporan dan siapa yang membuat laporan + + + + + + + Pelapor + + + + + + + + + Penerima Laporan + + + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginVertical: 10, + marginHorizontal: 10 + }, +}); diff --git a/src/screens/incident-page/stepComponent/time.js b/src/screens/incident-page/stepComponent/time.js new file mode 100644 index 0000000..5c6fb7f --- /dev/null +++ b/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 ( + <> + + + + + Laporan Awal Insiden + + + Wajib melaporkan maksimal 1x3 jam setelah kejadian + + + + + + } + /> + + + } + /> + + + + } + /> + + + } + /> + + + {showIncidentDatePicker && ( + + )} + {showIncidentTimePicker && ( + + )} + {showReportDatePicker && ( + + )} + {showReportTimePicker && ( + + )} + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + marginVertical: 10, + marginHorizontal: 10 + }, +});