Browse Source

refactor(Incident Report) : get and post data

master
farhantock 2 months ago
parent
commit
0f8f5d1f3c
  1. 138
      src/screens/incident-page/dialogForm.js
  2. 117
      src/screens/incident-page/index.js
  3. 228
      src/screens/incident-page/stepComponent/chronology.js
  4. 107
      src/screens/incident-page/stepComponent/containedAction.js
  5. 182
      src/screens/incident-page/stepComponent/incident.js
  6. 147
      src/screens/incident-page/stepComponent/location.js
  7. 208
      src/screens/incident-page/stepComponent/media.js
  8. 71
      src/screens/incident-page/stepComponent/report.js
  9. 35
      src/screens/incident-page/stepComponent/time.js

138
src/screens/incident-page/dialogForm.js

@ -1,6 +1,6 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { StatusBar } from 'react-native'; import { BackHandler, StatusBar } from 'react-native';
import { Button, Text, Appbar, } from 'react-native-paper'; import { Button, Dialog, Appbar, useTheme, Text, } from 'react-native-paper';
import { StyleSheet, View, } from 'react-native'; import { StyleSheet, View, } from 'react-native';
import { colors } from '../../utils/color'; import { colors } from '../../utils/color';
import { strings } from '../../utils/i18n'; import { strings } from '../../utils/i18n';
@ -11,10 +11,97 @@ import ReportScreen from './stepComponent/report';
import IncidentScreen from './stepComponent/incident'; import IncidentScreen from './stepComponent/incident';
import ChronologyScreen from './stepComponent/chronology'; import ChronologyScreen from './stepComponent/chronology';
import MediaScreen from './stepComponent/media'; import MediaScreen from './stepComponent/media';
import { useFocusEffect } from '@react-navigation/native';
import { resetIncidentReport } from '../../appredux/actions';
import { useDispatch } from 'react-redux';
import {
setIncidentDate, setIncidentTime, setReportDate, setReportTime, setDataRecepient, setDataReporter,
setArea, setIncidentLocation, setReportLocation, setDataIncident, setDataAchievement, setDataTypeRisk,
setIncidentRiskPotential, setRiskLevel, setChronology, setRootCase, setDataPrevention
} from '../../appredux/modules/incidentReport/actions';
export default function DialogForm({ route, navigation }) { export default function DialogForm({ route, navigation }) {
const theme = useTheme()
const dispatch = useDispatch()
const isDarkTheme = theme.dark
const [active, setActive] = useState(0); const [active, setActive] = useState(0);
const labels = ["Waktu", "Lokasi", "Pelapor", "Kejadian", "Kronologis", "Media"]; const [visible, setVisible] = useState(false);
const showDialog = () => setVisible(true);
const hideDialog = () => setVisible(false);
const labels = [strings('incidentReport.timeStep'), strings('incidentReport.locationStep'), strings('incidentReport.reportStep'), strings("incidentReport.incidentStep"), strings('incidentReport.chronologyStep'), "Media"];
useEffect(() => {
if (route.params?.item) {
const { item } = route.params;
console.log("item.incidentDate", item.info);
dispatch(setIncidentDate(item.info.incidentDate));
dispatch(setReportDate(item.info.reportDate));
dispatch(setDataRecepient(item.info.dataRecipient));
dispatch(setDataReporter(item.info.dataReporter));
dispatch(setArea(item.info.areaType));
dispatch(setIncidentLocation(item.info.incidentLocation));
dispatch(setReportLocation(item.info.reportLocation));
dispatch(setDataIncident(item.info.incidentList));
dispatch(setDataAchievement(item.info.achievement));
dispatch(setIncidentRiskPotential(item.info.incidentRiskPotential));
dispatch(setRiskLevel(item.info.riskLevel));
dispatch(setChronology(item.info.chronology));
dispatch(setRootCase(item.info.rootCase));
dispatch(setDataPrevention(item.info.prevention));
dispatch(setDataTypeRisk(item.info.typeRiskIncident));
}
}, [route.params?.item]);
useFocusEffect(
useCallback(() => {
const onBackPress = () => {
if (active === 0) {
showDialog();
return true; // prevent default behavior (navigation back)
} else {
setActive(prevActive => prevActive - 1);
return true;
}
};
BackHandler.addEventListener('hardwareBackPress', onBackPress);
return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress);
}, [active])
);
const handleSendIncidentReport = async () => {
const payload = {
project_charter_id: projectCharter,
info: {
achievement: dataAchievment,
incidentList: dataIncidentReport,
incidentDate: incidentDate,
reportDate: reportDate,
incidentLocation: incidentLocation,
reportLocation: reportLocation,
prevention: dataPrevention,
incidentRiskPotential: incidentRiskPotential,
areaType: area,
chronology: chronology,
rootCase: rootCase,
riskLevel: riskLevel,
supervisor: supervisor,
reporter: reporter,
typeRiskIncident: typeRiskIncident,
date: date,
dataReporter: dataReporter,
dataRecipient: dataRecipient,
etc: otherText,
etcAchievment: otherTextAchievment
},
status: statusReport
};
}
const renderStepContent = useMemo(() => { const renderStepContent = useMemo(() => {
switch (active) { switch (active) {
@ -47,13 +134,13 @@ export default function DialogForm({ route, navigation }) {
} }
}; };
return ( return (
<View style={styles.container}> <View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<StatusBar backgroundColor={colors.beanRed} barStyle="light-content" /> <StatusBar backgroundColor={colors.beanRed} barStyle="light-content" />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}> <Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}>
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} /> <Appbar.BackAction color={colors.pureWhite} onPress={showDialog} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('incidentReport.title')} /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('incidentReport.title')} />
</Appbar.Header> </Appbar.Header>
<View style={{ width: '100%', marginTop: 15, marginBottom: 2, backgroundColor: colors.pureWhite }}> <View style={{ width: '100%', marginTop: 15, marginBottom: 2, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}>
<StepIndicator <StepIndicator
stepCount={labels.length} stepCount={labels.length}
customStyles={stepIndicatorStyles} customStyles={stepIndicatorStyles}
@ -71,17 +158,34 @@ export default function DialogForm({ route, navigation }) {
</Button> </Button>
{active === labels.length - 1 ? {active === labels.length - 1 ?
( (
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}> <Button mode="contained" textColor={isDarkTheme ? theme.colors.surface : theme.colors.pureWhite} style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}>
Simpan Simpan
</Button> </Button>
) )
: ( : (
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={handleNext}> <Button mode="contained" textColor={isDarkTheme ? theme.colors.surface : theme.colors.pureWhite} style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={handleNext}>
Lanjut Lanjut
</Button> </Button>
)} )}
</View> </View>
<Dialog visible={visible} onDismiss={hideDialog} style={[styles.dialog, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<Dialog.Title style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>{strings('global.confirm')}</Dialog.Title>
<Dialog.Content>
<Text variant="bodyLarge" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>{strings('global.backMessage')}</Text>
<Text variant="bodyMedium" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>{strings('global.message')}</Text>
<Button buttonColor={colors.blue} textColor={theme.colors.pureWhite} style={{ borderRadius: 10, marginVertical: 20, paddingVertical: 5 }} contentStyle={{ flexDirection: 'row-reverse' }} mode="contained" onPress={() => {
dispatch(resetIncidentReport());
navigation.goBack();
}}>
{strings('global.yes')}
</Button>
<Button style={{ borderRadius: 10, paddingVertical: 5 }} contentStyle={{ flexDirection: 'row-reverse' }} textColor={isDarkTheme ? theme.colors.semiBlue : theme.colors.blue} mode="outlined" onPress={hideDialog}>
{strings('global.no')}
</Button>
</Dialog.Content>
</Dialog>
</View > </View >
) )
} }
@ -90,7 +194,6 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 20, marginTop: 20,
backgroundColor: colors.pureWhite
}, },
cardView: { cardView: {
marginTop: 10, marginTop: 10,
@ -114,7 +217,14 @@ const styles = StyleSheet.create({
padding: 10, padding: 10,
paddingBottom: 20, paddingBottom: 20,
}, },
dialog: {
marginVertical: 20,
padding: 20,
marginHorizontal: 20,
borderRadius: 10,
justifyContent: "center",
alignItems: "center"
}
}) })
const stepIndicatorStyles = { const stepIndicatorStyles = {
@ -133,12 +243,12 @@ const stepIndicatorStyles = {
stepIndicatorFinishedColor: colors.beanRed, stepIndicatorFinishedColor: colors.beanRed,
stepIndicatorUnFinishedColor: colors.semiRed, stepIndicatorUnFinishedColor: colors.semiRed,
stepIndicatorCurrentColor: colors.beanRed, stepIndicatorCurrentColor: colors.beanRed,
stepIndicatorLabelFontSize: 12, stepIndicatorLabelFontSize: 11,
currentStepIndicatorLabelFontSize: 12, currentStepIndicatorLabelFontSize: 11,
stepIndicatorLabelCurrentColor: colors.pureWhite, stepIndicatorLabelCurrentColor: colors.pureWhite,
stepIndicatorLabelFinishedColor: colors.pureWhite, stepIndicatorLabelFinishedColor: colors.pureWhite,
stepIndicatorLabelUnFinishedColor: colors.beanRed, stepIndicatorLabelUnFinishedColor: colors.beanRed,
labelColor: colors.semiRed, labelColor: colors.semiRed,
labelSize: 12, labelSize: 11,
currentStepLabelColor: colors.beanRed currentStepLabelColor: colors.beanRed
} }

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

@ -4,24 +4,77 @@ import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions, Status
import { colors } from '../../utils/color'; import { colors } from '../../utils/color';
import Icon from 'react-native-vector-icons/AntDesign'; import Icon from 'react-native-vector-icons/AntDesign';
import { strings } from '../../utils/i18n'; import { strings } from '../../utils/i18n';
import RequestModule from '../../services/api/request';
import moment from 'moment';
import 'moment/locale/id';
import { useSelector } from 'react-redux';
moment.locale('id');
const PAGE_SIZE = 25;
export default function IncidentScreen({ route, navigation }) { export default function IncidentScreen({ route, navigation }) {
const incidentData = [ const theme = useTheme();
{ id: 1, date: "Senin, 02-05-2024 21:03", location: "Kangean Site Indonesia", company: "Kangean Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "KEI2301M" }, const isDarkTheme = theme.dark;
{ id: 2, date: "Selasa, 03-05-2024 10:15", location: "Sumatra Site Indonesia", company: "Sumatra Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2402M" }, const { user } = useSelector(state => state.userReducer);
{ id: 3, date: "Rabu, 04-05-2024 15:20", location: "Java Site Indonesia", company: "Java Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "JEI2503M" }, const request = new RequestModule('incident-report');
{ id: 4, date: "Kamis, 05-05-2024 18:30", location: "Bali Site Indonesia", company: "Bali Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "BEI2604M" }, const [refreshing, setRefreshing] = useState(false);
{ id: 5, date: "Jumat, 06-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" }, const [page, setPage] = useState(0);
{ id: 5, date: "Sabtu, 07-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" }, const [dataIncident, setDataIncident] = useState([]);
{ id: 5, date: "Minggu, 08-05-2024 08:00", location: "Sulawesi Site Indonesia", company: "Sulawesi Energy Indonesia, BUT. Ltd.", icn: "KEI2301M", sicn: "SEI2705M" } const [loading, setLoading] = useState(false);
];
const onRefresh = useCallback(() => {
setRefreshing(true);
setPage(0);
getIncidentData(0);
}, []);
useEffect(() => {
getIncidentData(page);
}, [page]);
const getIncidentData = async (pageNum) => {
if (loading) return;
setLoading(true);
const payload = {
paging: { start: pageNum * PAGE_SIZE, length: PAGE_SIZE },
joins: [
{ name: 'project_charter', column_join: 'project_charter_id', column_results: ['sicn', 'project_name', 'icn', 'regional', 'client_name'] },
{ name: "m_regional", column_join: "id", references: "project_charter.regional", column_results: ['name'] },
],
columns: [
{ name: 'project_charter_id', logic_operator: '=', value: user?.assigment_hr?.project_charter_id.toString(), operator: 'AND' },
{ name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND' },
],
orders: { columns: ['created_at', 'id'], ascending: false },
};
const result = await request.getDataSearch(payload);
setDataIncident((prevData) =>
pageNum === 0 ? result.data.data : [...prevData, ...result.data.data]
);
setPage(pageNum + 1);
setRefreshing(false);
setLoading(false);
};
const handleScroll = ({ nativeEvent }) => {
if (isCloseToBottom(nativeEvent)) {
getPresenceHistory(page);
}
};
const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
};
const handleEdit = (item) => {
navigation.navigate('DialogFormIncident', { item });
};
return ( return (
<View style={styles.container}> <View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<StatusBar backgroundColor={colors.white} barStyle='dark-content' translucent={true} /> <StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.pureWhite} barStyle={isDarkTheme ? 'light-content' : 'dark-content'} translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.pureWhite, elevation: 4 }}> <Appbar.Header mode='center-aligned' style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite, elevation: 4 }}>
<Appbar.BackAction onPress={() => { navigation.goBack() }} /> <Appbar.BackAction onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} title={strings('incidentReport.title')} /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} title={strings('incidentReport.title')} />
</Appbar.Header> </Appbar.Header>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}> <View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
@ -29,23 +82,34 @@ export default function IncidentScreen({ route, navigation }) {
{strings('incidentReport.add')} {strings('incidentReport.add')}
</Button> </Button>
</View> </View>
<ScrollView
<ScrollView style={{ backgroundColor: colors.pureWhite }}> style={{ flex: 1, marginTop: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}
{incidentData.map((incident) => ( refreshControl={
<TouchableRipple key={incident.id} onPress={() => { navigation.navigate('DialogFormIncident') }}> <RefreshControl
<Card key={incident.id} elevation={2} style={styles.card}> refreshing={refreshing}
onRefresh={onRefresh}
colors={[colors.blue]}
progressBackgroundColor={colors.pureWhite}
/>
}
onScroll={handleScroll}
scrollEventThrottle={400}
>
{dataIncident.map((incident) => (
<TouchableRipple key={incident.id} onPress={() => handleEdit(incident)}>
<Card key={incident.id} elevation={2} style={[styles.card, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<View style={styles.cardContent}> <View style={styles.cardContent}>
<View style={styles.leftContent}> <View style={styles.leftContent}>
<Icon name="warning" size={25} color={colors.blue} /> <Icon name="warning" size={25} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.blue} />
</View> </View>
<View style={styles.midContent}> <View style={styles.midContent}>
<Text variant="bodySmall" style={styles.subText}>{incident.date}</Text> <Text variant="bodySmall" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{moment(incident.created_at).format('dddd, DD-MM-YYYY HH:mm')}</Text>
<Text variant="headlineMedium" style={styles.Text}>{incident.location}</Text> <Text variant="headlineMedium" style={[styles.Text, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{incident.join.project_charter_project_name}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.company}</Text> <Text variant="bodySmall" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{incident.join.project_charter_client_name}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.icn} - {incident.sicn}</Text> <Text variant="bodySmall" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{incident.join.project_charter_icn} - {incident.join.project_charter_sicn}</Text>
</View> </View>
<View style={styles.rightContent}> <View style={styles.rightContent}>
<Icon name="right" size={25} color={colors.blue} /> <Icon name="right" size={25} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.blue} />
</View> </View>
</View> </View>
</Card> </Card>
@ -60,22 +124,19 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 20, marginTop: 20,
backgroundColor: colors.pureWhite
}, },
Text: { Text: {
fontSize: 16, fontSize: 16,
fontWeight: 'bold', fontWeight: 'bold',
color: colors.blue
}, },
card: { card: {
flex: 1, flex: 1,
marginHorizontal: 8, marginHorizontal: 8,
marginVertical: 5, marginVertical: 5,
backgroundColor: colors.pureWhite,
}, },
subText: { subText: {
fontSize: 12, fontSize: 12,
color: colors.blue
}, },
cardContent: { cardContent: {
flexDirection: 'row', flexDirection: 'row',

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

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
import { List, TouchableRipple, TextInput, Card, Text, Button } from 'react-native-paper'; import { List, TouchableRipple, TextInput, Card, Text, Button, useTheme } from 'react-native-paper';
import { View, StyleSheet, ScrollView } from 'react-native'; import { View, StyleSheet, ScrollView, useWindowDimensions } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment'; import moment from 'moment';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
@ -11,12 +11,26 @@ import {
BottomSheetScrollView BottomSheetScrollView
} from '@gorhom/bottom-sheet'; } from '@gorhom/bottom-sheet';
import { strings } from '../../../utils/i18n'; import { strings } from '../../../utils/i18n';
import { useDispatch, useSelector } from 'react-redux';
import { RichEditor, RichToolbar, actions } from 'react-native-pell-rich-editor';
import { setIncidentRiskPotential, setRiskLevel, setChronology, setRootCase, setDataPrevention } from '../../../appredux/actions';
export default function ChronologyScreen({ route, navigation }) { export default function ChronologyScreen({ route, navigation }) {
const [chronology, setChronology] = useState('') const theme = useTheme();
const [incidentRiskPotential, setIncidentRiskPotential] = useState({ impact: "", likelihood: "", value: "", impactDescription: "", likelihoodDescription: "" }) const isDarkTheme = theme.dark;
const [riskLevel, setRiskLevel] = useState({ level: "", backgroundColor: '', color: '' }) const dispatch = useDispatch();
const colorsheet = isDarkTheme ? theme.colors.black : theme.colors.pureWhite;
const typeRiskIncident = useSelector(state => state.incidentReportReducer.typeRiskIncident);
const riskLevel = useSelector(state => state.incidentReportReducer.riskLevel);
const incidentRiskPotential = useSelector(state => state.incidentReportReducer.incidentRiskPotential);
const chronology = useSelector(state => state.incidentReportReducer.chronology);
const rootCase = useSelector(state => state.incidentReportReducer.rootCase);
const dataPrevention = useSelector(state => state.incidentReportReducer.dataPrevention);
const bottomSheetModalImpactRef = useRef(null); const bottomSheetModalImpactRef = useRef(null);
const bottomSheetModalLikelihoodRef = useRef(null); const bottomSheetModalLikelihoodRef = useRef(null);
const chronologyText = useRef();
const rootcaseText = useRef();
const snapPoints = useMemo(() => ['25%', '50%'], []); const snapPoints = useMemo(() => ['25%', '50%'], []);
@ -28,6 +42,20 @@ export default function ChronologyScreen({ route, navigation }) {
bottomSheetModalLikelihoodRef.current?.present(); bottomSheetModalLikelihoodRef.current?.present();
}, []); }, []);
// useEffect(() => {
// if (route.params?.isSaved) {
// Toast.show({ type: 'success', text1: 'Berhasil di simpan' }); // Show success toast
// }
// }, [route.params?.isSaved]);
const handleEdit = (item) => {
navigation.navigate('ContainedActionScreen', { item });
};
const handleDelete = (id) => {
const updatedData = dataPrevention.filter(prevention => prevention.id !== id);
dispatch(setDataPrevention(updatedData));
};
useEffect(() => { useEffect(() => {
if (incidentRiskPotential.impact !== "" && incidentRiskPotential.likelihood !== "") { if (incidentRiskPotential.impact !== "" && incidentRiskPotential.likelihood !== "") {
@ -37,48 +65,39 @@ export default function ChronologyScreen({ route, navigation }) {
const val = impactValue * likelihoodValue; const val = impactValue * likelihoodValue;
let newRiskLevel, backgroundColor, color; let newRiskLevel, backgroundColor, color;
if (val >= 1 && val <= 5) { if (val >= 1 && val <= 5) {
backgroundColor = '#E3F8E8' backgroundColor = colors.semigreen
color = '#17C13E' color = colors.green
newRiskLevel = 'Rendah'; newRiskLevel = 'Rendah';
} else if (val >= 6 && val <= 10) { } else if (val >= 6 && val <= 10) {
backgroundColor = "#F8DC49" backgroundColor = colors.yellow
color = "white" color = "white"
newRiskLevel = 'Sedang'; newRiskLevel = 'Sedang';
} else if (val >= 11 && val <= 15) { } else if (val >= 11 && val <= 15) {
backgroundColor = '#FFD9AF' backgroundColor = colors.semiRed
color = '#EC9C3D' color = colors.beanRed
newRiskLevel = 'Tinggi'; newRiskLevel = 'Tinggi';
} else { } else {
backgroundColor = '#FFC5C3' backgroundColor = colors.red
color = '#D9534F' color = colors.white
newRiskLevel = 'Bencana'; newRiskLevel = 'Bencana';
} }
if (incidentRiskPotential.value !== val.toString() || riskLevel.level !== newRiskLevel || riskLevel.backgroundColor !== backgroundColor || riskLevel.color !== color) { if (incidentRiskPotential.value !== val.toString() || riskLevel.level !== newRiskLevel || riskLevel.backgroundColor !== backgroundColor || riskLevel.color !== color) {
setIncidentRiskPotential(prevState => ({ ...prevState, value: val.toString() })); dispatch(setIncidentRiskPotential({ ...incidentRiskPotential, value: val.toString() }));
setRiskLevel({ level: newRiskLevel, backgroundColor: backgroundColor, color: color }); dispatch(setRiskLevel({ level: newRiskLevel, backgroundColor: backgroundColor, color: color }));
} }
} }
}, [incidentRiskPotential, riskLevel]); }, [typeRiskIncident, incidentRiskPotential, riskLevel]);
const handleSelectImpact = (impact) => { const handleSelectImpact = (impact) => {
setIncidentRiskPotential(prevState => ({ dispatch(setIncidentRiskPotential({ ...incidentRiskPotential, impact: impact.label, value: impact.value, impactDescription: impact.description }));
...prevState,
impact: impact.label,
value: impact.value,
impactDescription: impact.description
}));
bottomSheetModalImpactRef.current?.dismiss(); bottomSheetModalImpactRef.current?.dismiss();
}; };
const handleSelectLikelihood = (likelihood) => { const handleSelectLikelihood = (likelihood) => {
setIncidentRiskPotential(prevState => ({ dispatch(setIncidentRiskPotential({ ...incidentRiskPotential, likelihood: likelihood.label, likelihoodDescription: likelihood.description }));
...prevState,
likelihood: likelihood.label,
likelihoodDescription: likelihood.description
}));
bottomSheetModalLikelihoodRef.current?.dismiss(); bottomSheetModalLikelihoodRef.current?.dismiss();
}; };
@ -118,6 +137,7 @@ export default function ChronologyScreen({ route, navigation }) {
<View> <View>
<TouchableRipple key={dataTranslasiImpact[data.label]} onPress={() => handleSelectImpact(data)}> <TouchableRipple key={dataTranslasiImpact[data.label]} onPress={() => handleSelectImpact(data)}>
<List.Item <List.Item
key={data.label}
title={dataTranslasiImpact[data.label]} title={dataTranslasiImpact[data.label]}
/> />
</TouchableRipple> </TouchableRipple>
@ -127,54 +147,77 @@ export default function ChronologyScreen({ route, navigation }) {
<View> <View>
<TouchableRipple key={dataTranslasiLikelihood[data.label]} onPress={() => handleSelectLikelihood(data)}> <TouchableRipple key={dataTranslasiLikelihood[data.label]} onPress={() => handleSelectLikelihood(data)}>
<List.Item <List.Item
key={data.label}
title={dataTranslasiLikelihood[data.label]} title={dataTranslasiLikelihood[data.label]}
/> />
</TouchableRipple> </TouchableRipple>
</View> </View>
); );
return ( return (
<> <>
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> <Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}>
<View> <View>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> <Text variant="bodyLarge" style={{ color: colors.beanRed, fontWeight: 'bold' }}>
Kronologis kejadian Kronologis kejadian
</Text> </Text>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> <Text variant="bodySmall" style={{ color: colors.beanRed }}>
Jelaskan bagaimana terjadinya insiden Jelaskan bagaimana terjadinya insiden
</Text> </Text>
</View> </View>
</Card> </Card>
<ScrollView> <ScrollView>
<View style={styles.container}> <View style={styles.container}>
<TextInput <Text variant="bodyLarge" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>
dense={true} Kronologis Singkat Kejadian
underlineColor={colors.amethystSmoke} </Text>
activeOutlineColor={colors.blue} <RichEditor
label="Kronologis Singkat Kejadian" ref={chronologyText}
mode="outlined" placeholder='Kronologis Singkat Kejadian'
placeholder='Isi kronologis' disabled={false}
multiline={true} containerStyle={{ backgroundColor: colorsheet, borderWidth: 1, borderColor: colors.colorsheet }}
numberOfLines={5} initialContentHTML={chronology}
value={chronology} editorStyle={{ backgroundColor: colorsheet, color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
onChangeText={(text) => { onChange={(text) => {
setChronology(text) dispatch(setChronology(text));
}} }}
/> />
<RichToolbar
<TextInput editor={chronologyText}
dense={true} actions={[actions.setBold, actions.setItalic, actions.setUnderline, actions.insertOrderedList,
underlineColor={colors.amethystSmoke} actions.insertBulletsList]}
activeOutlineColor={colors.blue} iconTint={isDarkTheme ? theme.colors.pureWhite : theme.colors.black}
mode="outlined" selectedIconTint={isDarkTheme ? theme.colors.pureWhite : theme.colors.black}
label="Penjelasan Permasalahan" selectedButtonStyle={{ backgroundColor: colors.blue }}
placeholder='Isi Permasalahan' style={{ backgroundColor: colorsheet, borderColor: colors.colorsheet, marginBottom: 10 }}
multiline={true} />
numberOfLines={5} <Text variant="bodyLarge" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>
onChangeText={(text) => { Penjelasan Singkat Kejadian
setChronology(text) </Text>
<RichEditor
ref={rootcaseText}
placeholder='Kronologis Singkat Kejadian'
disabled={false}
containerStyle={{ backgroundColor: colorsheet, borderWidth: 1, borderColor: colors.colorsheet }}
initialContentHTML={rootCase}
editorStyle={{ backgroundColor: colorsheet, color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
onChange={(text) => {
dispatch(setRootCase(text));
}} }}
/> />
<RichToolbar
editor={rootcaseText}
actions={[actions.setBold, actions.setItalic, actions.setUnderline, actions.insertOrderedList,
actions.insertBulletsList]}
iconTint={isDarkTheme ? theme.colors.pureWhite : theme.colors.black}
selectedIconTint={isDarkTheme ? theme.colors.pureWhite : theme.colors.black}
selectedButtonStyle={{ backgroundColor: colors.blue }}
style={{ backgroundColor: colorsheet, borderColor: colors.colorsheet, marginBottom: 10 }}
/>
<TouchableRipple onPress={handleOpenImpact}> <TouchableRipple onPress={handleOpenImpact}>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -189,8 +232,8 @@ export default function ChronologyScreen({ route, navigation }) {
/> />
</TouchableRipple> </TouchableRipple>
{incidentRiskPotential.impactDescription && ( {incidentRiskPotential.impactDescription && (
<Card style={{ backgroundColor: colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> <Card style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}>
<Text variant="bodySmall" style={{ fontSize: 12 }}> <Text variant="bodySmall" style={{ fontSize: 12, color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>
{incidentRiskPotential.impactDescription} {incidentRiskPotential.impactDescription}
</Text> </Text>
</Card> </Card>
@ -209,50 +252,54 @@ export default function ChronologyScreen({ route, navigation }) {
</TouchableRipple> </TouchableRipple>
{incidentRiskPotential.likelihoodDescription && ( {incidentRiskPotential.likelihoodDescription && (
<Card style={{ backgroundColor: colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> <Card style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}>
<Text variant="bodySmall" style={{ fontSize: 12 }}> <Text variant="bodySmall" style={{ fontSize: 12, color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>
{incidentRiskPotential.likelihoodDescription} {incidentRiskPotential.likelihoodDescription}
</Text> </Text>
</Card> </Card>
)} )}
<View style={styles.row}> <View style={styles.row}>
<Card style={{ flex: 1, marginRight: 5, backgroundColor: '#FFC5C3', paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> {typeRiskIncident &&
<Text variant="bodySmall" style={{ fontSize: 12, color: '#D9534F' }}> <Card style={{ flex: 1, marginRight: 5, backgroundColor: typeRiskIncident === 'Critical' ? colors.beanRed : typeRiskIncident === 'Major' ? colors.yellow : 'black', paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}>
Critical <Text variant="bodySmall" style={{ fontSize: 12, color: typeRiskIncident === 'Critical' ? colors.red : typeRiskIncident === 'Major' ? colors.black : 'black' }}>
</Text> {typeRiskIncident}
</Card> </Text>
<Card style={{ flex: 1, marginLeft: 5, backgroundColor: riskLevel.backgroundColor, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}> </Card>
<Text variant="bodySmall" style={{ fontSize: 12, color: riskLevel.color }}> }
{riskLevel.level} {incidentRiskPotential.value} {incidentRiskPotential.value && riskLevel.level &&
</Text> <Card style={{ flex: 1, marginLeft: 5, backgroundColor: riskLevel.backgroundColor, paddingVertical: 15, paddingHorizontal: 15, marginVertical: 5, borderRadius: 5 }}>
</Card> <Text variant="bodySmall" style={{ fontSize: 12, color: riskLevel.color }}>
{riskLevel.level} {incidentRiskPotential.value}
</Text>
</Card>
}
</View> </View>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 14 }}>
Rencana tindakan pencegahan Rencana tindakan pencegahan
</Text> </Text>
{dataPrevention.map(item => (
<Card elevation={2} style={styles.card}> <Card elevation={2} style={[styles.card, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<View style={styles.cardContent}> <View style={styles.cardContent}>
<View style={styles.leftContent}> <View style={styles.leftContent}>
<Icon name="view-list" size={25} color={colors.blue} /> <Icon name="view-list" size={25} color={colors.blue} />
</View>
<View style={styles.midContent}>
<Text variant="bodySmall" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }]}>{moment(item.date).format('DD-MM-YYYY')}</Text>
<Text variant="bodyMedium" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }]}>{item.status}</Text>
<Text variant="headlineMedium" style={[styles.Text, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }]}>{item.who}</Text>
<Text variant="bodySmall" style={[styles.subText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }]}>{item.plan}</Text>
</View>
</View> </View>
<View style={styles.midContent}> <View style={styles.buttonContainer}>
<Text variant="bodySmall" style={styles.subText}>01-03-2024</Text> <Button mode="outlined" style={styles.button} textColor={colors.beanRed} onPress={() => handleEdit(item)}>
<Text variant="headlineMedium" style={styles.Text}>Farhan</Text> Edit
<Text variant="bodySmall" style={styles.subText}>Melakukan pengamatan dan patroli area pagar luar Depo MRT</Text> </Button>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} textColor={isDarkTheme ? theme.colors.black : theme.colors.pureWhite} onPress={() => handleDelete(item.id)}>
Hapus
</Button>
</View> </View>
</View> </Card>
<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> </View>
</ScrollView> </ScrollView>
<Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginHorizontal: 8, }} textColor={colors.blue} mode="contained-tonal" onPress={() => { navigation.navigate('ContainedActionScreen') }}> <Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, marginHorizontal: 8, }} textColor={colors.blue} mode="contained-tonal" onPress={() => { navigation.navigate('ContainedActionScreen') }}>
@ -263,6 +310,7 @@ export default function ChronologyScreen({ route, navigation }) {
ref={bottomSheetModalImpactRef} ref={bottomSheetModalImpactRef}
index={1} index={1}
snapPoints={snapPoints} snapPoints={snapPoints}
backgroundStyle={{ backgroundColor: colorsheet }}
> >
<BottomSheetScrollView contentContainerStyle={styles.scrollView}> <BottomSheetScrollView contentContainerStyle={styles.scrollView}>
{dataSelectImpact.map(item => renderImpact(item))} {dataSelectImpact.map(item => renderImpact(item))}
@ -274,6 +322,7 @@ export default function ChronologyScreen({ route, navigation }) {
ref={bottomSheetModalLikelihoodRef} ref={bottomSheetModalLikelihoodRef}
index={1} index={1}
snapPoints={snapPoints} snapPoints={snapPoints}
backgroundStyle={{ backgroundColor: colorsheet }}
> >
<BottomSheetScrollView contentContainerStyle={styles.scrollView}> <BottomSheetScrollView contentContainerStyle={styles.scrollView}>
{dataSelectLikelihood.map(item => renderLikelihood(item))} {dataSelectLikelihood.map(item => renderLikelihood(item))}
@ -308,17 +357,14 @@ const styles = StyleSheet.create({
Text: { Text: {
fontSize: 16, fontSize: 16,
fontWeight: 'bold', fontWeight: 'bold',
color: colors.blue
}, },
card: { card: {
flex: 1, flex: 1,
marginHorizontal: 8, marginHorizontal: 8,
marginVertical: 5, marginVertical: 5,
backgroundColor: colors.pureWhite,
}, },
subText: { subText: {
fontSize: 12, fontSize: 12,
color: colors.blue
}, },
row: { row: {
flexDirection: 'row', flexDirection: 'row',

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

@ -1,47 +1,89 @@
import React, { useState, useRef, useMemo, useCallback } from 'react'; import React, { useState, useRef, useCallback, useEffect } from 'react';
import { TouchableRipple, TextInput, Appbar, List, Button } from 'react-native-paper'; import { TouchableRipple, TextInput, Appbar, List, Button, useTheme } from 'react-native-paper';
import { View, StyleSheet, ScrollView } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment'; import moment from 'moment';
import { colors } from '../../../utils/color'; import { colors } from '../../../utils/color';
import { strings } from '../../../utils/i18n'; import { strings } from '../../../utils/i18n';
import { import { BottomSheetModal, BottomSheetModalProvider, BottomSheetView } from '@gorhom/bottom-sheet';
BottomSheetModal, import { useDispatch, useSelector } from 'react-redux';
BottomSheetModalProvider, import { setDataPrevention } from '../../../appredux/actions';
BottomSheetView
} from '@gorhom/bottom-sheet';
export default function ReportScreen({ route, navigation }) { export default function ReportScreen({ route, navigation }) {
const theme = useTheme();
const isDarkTheme = theme.dark;
const dispatch = useDispatch();
const colorsheet = isDarkTheme ? theme.colors.black : theme.colors.pureWhite;
const dataPrevention = useSelector(state => state.incidentReportReducer.dataPrevention);
const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false); const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false);
const [date, setDate] = useState(''); const [date, setDate] = useState(null);
const [status, setStatus] = useState('') const [status, setStatus] = useState('');
const [preventionPlan, setPreventionPlan] = useState('');
const [who, setWho] = useState('');
const [isEdit, setIsEdit] = useState(false);
const [editId, setEditId] = useState(null);
const bottomSheetModal = useRef(null); const bottomSheetModal = useRef(null);
useEffect(() => {
if (route.params?.item) {
const { item } = route.params;
console.log("item", item);
setPreventionPlan(item.plan);
setStatus(item.status);
setWho(item.who)
setDate(item.date)
setEditId(item.id);
setIsEdit(true)
}
}, [route.params?.item]);
const showIncidentDatePickerFunc = () => { const showIncidentDatePickerFunc = () => {
setShowIncidentDatePicker(true); setShowIncidentDatePicker(true);
}; };
// const snapPoints = useMemo(() => ['10%', ], []);
const handleOpenSheet = useCallback(() => { const handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present(); bottomSheetModal.current?.present();
}, []); }, []);
const handleStatus = (data) => { const handleStatus = (data) => {
setStatus(data) setStatus(data);
bottomSheetModal.current?.dismiss(); bottomSheetModal.current?.dismiss();
} };
const handleIncidentDateChange = (event, selectedDate) => { const handleIncidentDateChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowIncidentDatePicker(false); setShowIncidentDatePicker(false);
setDate(currentDate); if (selectedDate) {
setDate(selectedDate);
}
};
const handleSave = () => {
const newPreventionData = {
id: editId || dataPrevention.length + 1,
plan: preventionPlan,
who: who,
date: date,
status: status
};
if (isEdit) {
const updatedData = dataPrevention.map(item => item.id === editId ? newPreventionData : item);
dispatch(setDataPrevention(updatedData));
} else {
dispatch(setDataPrevention([...dataPrevention, newPreventionData]));
}
navigation.goBack();
}; };
return ( return (
<> <>
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}> <Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}>
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} /> <Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title='Rencana pencegahan' /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title='Rencana pencegahan' />
</Appbar.Header> </Appbar.Header>
<View style={styles.container}> <ScrollView style={{ backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}>
<ScrollView> <View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<TextInput <TextInput
dense={true} dense={true}
underlineColor={colors.amethystSmoke} underlineColor={colors.amethystSmoke}
@ -50,8 +92,9 @@ export default function ReportScreen({ route, navigation }) {
mode="outlined" mode="outlined"
multiline={true} multiline={true}
numberOfLines={5} numberOfLines={5}
value={preventionPlan}
onChangeText={setPreventionPlan}
/> />
<TextInput <TextInput
style={{ marginTop: 10, marginBottom: 15 }} style={{ marginTop: 10, marginBottom: 15 }}
dense={true} dense={true}
@ -60,6 +103,8 @@ export default function ReportScreen({ route, navigation }) {
mode="outlined" mode="outlined"
label='Who' label='Who'
placeholder='Who' placeholder='Who'
value={who}
onChangeText={setWho}
/> />
<TouchableRipple onPress={showIncidentDatePickerFunc}> <TouchableRipple onPress={showIncidentDatePickerFunc}>
<TextInput <TextInput
@ -68,7 +113,7 @@ export default function ReportScreen({ route, navigation }) {
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label=" When" label="When"
value={date ? moment(date).format("DD-MM-YYYY") : ''} value={date ? moment(date).format("DD-MM-YYYY") : ''}
editable={false} editable={false}
right={<TextInput.Icon icon="calendar" onPress={showIncidentDatePickerFunc} />} right={<TextInput.Icon icon="calendar" onPress={showIncidentDatePickerFunc} />}
@ -81,24 +126,22 @@ export default function ReportScreen({ route, navigation }) {
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label=" Status" label="Status"
value={status ? status : ''} value={status ? status : ''}
editable={false} editable={false}
right={<TextInput.Icon icon="list-status" onPress={handleOpenSheet} />} right={<TextInput.Icon icon="list-status" onPress={handleOpenSheet} />}
/> />
</TouchableRipple> </TouchableRipple>
</ScrollView > </View>
</View> </ScrollView>
<View style={[styles.buttonContainer, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<View style={styles.buttonContainer}> <Button mode="outlined" style={styles.button} textColor={colors.mistBlue} onPress={() => { navigation.goBack() }}>
<Button mode="outlined" style={styles.button} textColor={colors.mistBlue} onPress={() => { navigation.goBack() }} >
Kembali Kembali
</Button> </Button>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}> <Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} textColor={isDarkTheme ? theme.colors.black : theme.colors.pureWhite} onPress={handleSave}>
Simpan Simpan
</Button> </Button>
</View> </View>
{ {
showIncidentDatePicker && ( showIncidentDatePicker && (
<DateTimePicker <DateTimePicker
@ -109,7 +152,6 @@ export default function ReportScreen({ route, navigation }) {
/> />
) )
} }
<BottomSheetModalProvider> <BottomSheetModalProvider>
<BottomSheetModal <BottomSheetModal
ref={bottomSheetModal} ref={bottomSheetModal}
@ -117,26 +159,27 @@ export default function ReportScreen({ route, navigation }) {
snapPoints={['20%']} snapPoints={['20%']}
bottomInset={10} bottomInset={10}
detached={true} detached={true}
style={{ marginHorizontal: 15 }} backgroundStyle={{ backgroundColor: colorsheet }}
style={{ marginHorizontal: 5 }}
> >
<BottomSheetView > <BottomSheetView>
<TouchableRipple onPress={() => handleStatus("Open")}> <TouchableRipple onPress={() => handleStatus("Open")}>
<List.Item <List.Item
key='Open'
title="OPEN" title="OPEN"
left={props => <List.Icon {...props} icon="" />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple onPress={() => handleStatus("Close")}> <TouchableRipple onPress={() => handleStatus("Close")}>
<List.Item <List.Item
key='Close'
title="CLOSE" title="CLOSE"
left={props => <List.Icon {...props} icon="" />}
/> />
</TouchableRipple> </TouchableRipple>
</BottomSheetView> </BottomSheetView>
</BottomSheetModal> </BottomSheetModal>
</BottomSheetModalProvider> </BottomSheetModalProvider>
</> </>
) );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

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

@ -1,63 +1,193 @@
import React, { useState } from 'react'; import React, { useCallback, useRef, useState } from 'react';
import { TouchableRipple, Chip, Card, Text } from 'react-native-paper'; import { TouchableRipple, Chip, Card, Text, useTheme, Searchbar, List, Button } from 'react-native-paper';
import { View, StyleSheet, ScrollView } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment'; import moment from 'moment';
import { colors } from '../../../utils/color'; import { colors } from '../../../utils/color';
import { jenisKejadian } from '../../../config/ApiConst' import { jenisKejadian } from '../../../config/ApiConst'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons' import Icon from 'react-native-vector-icons/MaterialCommunityIcons'
import { setDataIncident, setDataAchievement, setDataTypeRisk } from '../../../appredux/actions';
import { useDispatch, useSelector } from 'react-redux';
import {
BottomSheetModal,
BottomSheetModalProvider,
BottomSheetScrollView
} from '@gorhom/bottom-sheet';
import { strings } from '../../../utils/i18n';
export default function IncidentScreen() { export default function IncidentScreen() {
const [selectedItems, setSelectedItems] = useState([]); const theme = useTheme();
const [dataIncidentReport, setDataIncidentReport] = useState(jenisKejadian[0].data) const isDarkTheme = theme.dark;
const [typeRiskIncident, setTypeRiskIncident] = useState("") const dispatch = useDispatch();
const [filter, setFilter] = useState('');
const bottomSheetIncindentModal = useRef(null);
const bottomSheetAchievementModal = useRef(null);
const dataIncidentReport = useSelector(state => state.incidentReportReducer.dataIncidentReport);
const dataAchievement = useSelector(state => state.incidentReportReducer.dataAchievement);
const colorsheet = isDarkTheme ? theme.colors.black : theme.colors.pureWhite;
const handleCheckboxChange = (item) => { const handleOpenSheetIncident = useCallback(() => {
bottomSheetIncindentModal.current?.present();
}, []);
const handleOpenSheetAchievement = useCallback(() => {
bottomSheetAchievementModal.current?.present();
}, []);
const handleSelectAchievement = (item) => {
const updatedDataAchievement = dataAchievement.map(dataItem => {
if (dataItem.label === item.label) {
return { ...dataItem, checked: !dataItem.checked };
}
return dataItem;
});
dispatch(setDataAchievement(updatedDataAchievement));
setFilter('');
bottomSheetAchievementModal.current?.dismiss();
}
const handleSelectIncident = (item) => {
const updatedDataIncidentReport = dataIncidentReport.map(dataItem => { const updatedDataIncidentReport = dataIncidentReport.map(dataItem => {
if (dataItem.label === item.label) { if (dataItem.label === item.label) {
return { ...dataItem, checked: !dataItem.checked }; return { ...dataItem, checked: !dataItem.checked };
} }
return dataItem; return dataItem;
}); });
setDataIncidentReport(updatedDataIncidentReport);
const hasCritical = updatedDataIncidentReport.some(item => item.level === 'Critical' && item.checked); const hasCritical = updatedDataIncidentReport.some(item => item.level === 'Critical' && item.checked);
setTypeRiskIncident(updatedDataIncidentReport.some(item => item.checked) ? (hasCritical ? 'Critical' : 'Major') : ''); dispatch(setDataTypeRisk(updatedDataIncidentReport.some(item => item.checked) ? (hasCritical ? 'Critical' : 'Major') : ''));
}; dispatch(setDataIncident(updatedDataIncidentReport));
setFilter('');
bottomSheetIncindentModal.current?.dismiss();
}
const dataFilterIncident = dataIncidentReport.filter(item => item.label.toLowerCase().includes(filter.toLowerCase()));
const dataFilterAchievement = dataAchievement.filter(item => item.label.toLowerCase().includes(filter.toLowerCase()));
const isItemSelected = (label) => { const renderAchievement = (data) => (
return dataIncidentReport.some(item => item.label === label && item.checked); <View key={data.label}>
}; <TouchableRipple onPress={() => handleSelectAchievement(data)}>
<List.Item
title={data.label}
/>
</TouchableRipple>
</View>
);
const renderIncident = (data) => (
<View key={data.label}>
<TouchableRipple onPress={() => handleSelectIncident(data)}>
<List.Item
title={data.label}
/>
</TouchableRipple>
</View>
);
return ( return (
<> <>
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}> <Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}>
<View> <View>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}> <Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}>
Jenis kejadian {strings('incidentReport.achievement')} & {strings('incidentReport.incident')}
</Text> </Text>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}> <Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}>
Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu Capaian/Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu
</Text> </Text>
</View> </View>
</Card> </Card>
<View style={styles.container}> <View style={styles.container}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16 }}>
Pilih Jenis Kejadian {strings('incidentReport.achievement')}
</Text>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, paddingVertical: 1, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleOpenSheetAchievement}>
{strings('incidentReport.achievementSelect')}
</Button>
</View>
<ScrollView style={styles.listData}>
<View>
{dataAchievement.filter(item => item.checked).map(item => (
<Chip
key={item.label}
icon={() => (
<Icon name='circle-slice-8' size={20} color={colors.blue} />
)} textStyle={{ color: colors.blue }} onPress={() => handleSelectAchievement(item)} style={{ backgroundColor: colors.semiBlue, marginBottom: 5 }}>{item.label}</Chip>
))}
</View>
</ScrollView>
<Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginTop: 5 }}>
{strings('incidentReport.incident')}
</Text> </Text>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Button icon="plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, paddingVertical: 1, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleOpenSheetIncident}>
{strings('incidentReport.incidentSelect')}
</Button>
</View>
<ScrollView style={styles.listData}> <ScrollView style={styles.listData}>
<View> <View>
{dataIncidentReport.map(item => ( {dataIncidentReport.filter(item => item.checked).map(item => (
<> <Chip
<Chip key={item.label}
icon={() => ( icon={() => (
<Icon name={isItemSelected(item.label) ? 'circle-slice-8' : 'circle-outline'} size={20} color={isItemSelected(item.label) ? colors.pureWhite : colors.blue} /> <Icon name='circle-slice-8' size={20} color={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 > )} textStyle={{ color: colors.blue }} onPress={() => handleSelectIncident(item)} style={{ backgroundColor: colors.semiBlue, marginBottom: 5 }}>{item.label}</Chip>
</>
))} ))}
</View> </View>
</ScrollView > </ScrollView>
</View > </View>
<BottomSheetModalProvider>
<BottomSheetModal
animateOnMount={true}
ref={bottomSheetIncindentModal}
index={0}
snapPoints={['90%']}
detached={true}
keyboardBehavior="fillParent"
backgroundStyle={{ backgroundColor: colorsheet }}
style={{ marginHorizontal: 5 }}
>
<Searchbar
mode='bar'
placeholder={strings('global.search')}
onChangeText={text => setFilter(text)}
value={filter}
traileringIconColor={isDarkTheme ? theme.colors.pureWhite : theme.colors.surface}
style={{ marginHorizontal: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.background }}
showDivider={true}
elevation={1}
/>
<BottomSheetScrollView contentContainerStyle={{ marginHorizontal: 5, marginTop: 3 }}>
{dataFilterIncident.map(item => renderIncident(item))}
</BottomSheetScrollView>
</BottomSheetModal>
<BottomSheetModal
animateOnMount={true}
ref={bottomSheetAchievementModal}
index={0}
snapPoints={['90%']}
detached={true}
keyboardBehavior="fillParent"
backgroundStyle={{ backgroundColor: colorsheet }}
style={{ marginHorizontal: 5 }}
>
<Searchbar
mode='bar'
placeholder={strings('global.search')}
onChangeText={text => setFilter(text)}
value={filter}
traileringIconColor={isDarkTheme ? theme.colors.pureWhite : theme.colors.surface}
style={{ marginHorizontal: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.background }}
showDivider={true}
elevation={1}
/>
<BottomSheetScrollView contentContainerStyle={{ marginHorizontal: 5, marginTop: 3 }}>
{dataFilterAchievement.map(item => renderAchievement(item))}
</BottomSheetScrollView>
</BottomSheetModal>
</BottomSheetModalProvider>
</> </>
) )
} }
@ -71,5 +201,5 @@ const styles = StyleSheet.create({
listData: { listData: {
flex: 1, flex: 1,
marginTop: 10, marginTop: 10,
}, }
}); });

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

@ -1,58 +1,44 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { TextInput, Card, Checkbox, Text, TouchableRipple } from 'react-native-paper'; import { TextInput, Card, Checkbox, Text, TouchableRipple, useTheme } from 'react-native-paper';
import { StyleSheet, View, ScrollView } from 'react-native'; import { StyleSheet, View, ScrollView } from 'react-native';
import { colors } from '../../../utils/color' import { colors } from '../../../utils/color'
import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux';
import { setArea, setIncidentLocation, setReportLocation } from '../../../appredux/modules/incidentReport/actions';
export default function LocationScreen() { export default function LocationScreen() {
const navigation = useNavigation(); const theme = useTheme();
const [selectedProject, setSelectedProject] = useState(null); const dispatch = useDispatch();
const [area, setArea] = useState({ external: false, internal: false }) const { user } = useSelector(state => state.userReducer);
const handleProjectPress = () => { const area = useSelector(state => state.incidentReportReducer.area);
navigation.navigate('SearchPage', { dummyData, onSelect: handleProjectSelect }); const incidentLocation = useSelector(state => state.incidentReportReducer.incidentLocation);
}; const reportLocation = useSelector(state => state.incidentReportReducer.reportLocation);
const isDarkTheme = theme.dark;
console.log("area", area);
const handleCheckboxArea = (data) => { const handleCheckboxArea = (data) => {
const { name } = data; const { name } = data;
setArea(prevState => ({ const updatedArea = { ...area, [name]: !area[name] };
...prevState, dispatch(setArea(updatedArea));
[name]: !prevState[name] };
}));
const handleIncidentLocationChange = (location) => {
dispatch(setIncidentLocation({ location }));
}; };
console.log("Area :", area);
const handleProjectSelect = (project) => { const handleIncidentSubLocationChange = (subLocation) => {
setSelectedProject(project); dispatch(setIncidentLocation({ subLocation }));
};
const handleReportProjectLocationChange = (projectLocation) => {
dispatch(setReportLocation({ projectLocation }));
}; };
const dummyData = [ const handleReportLocationChange = (reportLocation) => {
{ id: '1', name: 'MRT', icn: 'test1', sicn: 'test"1' }, dispatch(setReportLocation({ reportLocation }));
{ 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' }, const handleReportSubLocationChange = (subLocation) => {
{ id: '5', name: 'Jhonny', icn: 'test1', sicn: 'test"1' }, dispatch(setReportLocation({ subLocation }));
{ 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 ( return (
<> <>
@ -68,22 +54,20 @@ export default function LocationScreen() {
</Card> </Card>
<ScrollView> <ScrollView>
<View style={styles.container}> <View style={styles.container}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6 }}>
Lokasi Kejadian Lokasi Kejadian
</Text> </Text>
<TouchableRipple onPress={handleProjectPress}>
<TextInput <TextInput
dense={true} dense={true}
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
editable={false} editable={false}
label='Pilih Proyek' label='Proyek'
placeholder='Pilih Proyek' placeholder='Pilih Proyek'
value={selectedProject ? selectedProject.name : ''} value={user?.assigment_hr ? user?.assigment_hr?.join.project_charter_project_name : ''}
right={<TextInput.Icon icon="chevron-down" />} />
/>
</TouchableRipple>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -93,6 +77,8 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Lokasi Kejadian' label='Lokasi Kejadian'
placeholder='Lokasi Kejadian' placeholder='Lokasi Kejadian'
value={incidentLocation.location}
onChangeText={handleIncidentLocationChange}
/> />
<TextInput <TextInput
@ -103,49 +89,50 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Sub Lokasi Kejadian' label='Sub Lokasi Kejadian'
placeholder='Sub Lokasi Kejadian' placeholder='Sub Lokasi Kejadian'
value={incidentLocation.subLocation}
onChangeText={handleIncidentSubLocationChange}
/> />
<View style={styles.row}> <View style={styles.row}>
<Checkbox <Checkbox
status={area.internal ? 'checked' : 'unchecked'} status={area?.internal ? 'checked' : 'unchecked'}
color={colors.blue} color={colors.blue}
uncheckedColor={colors.amethystSmoke} uncheckedColor={colors.amethystSmoke}
onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}
/> />
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
Internal (Didalam area/kawasan/parimeter Project) Internal (Didalam area/kawasan/parimeter Project)
</Text> </Text>
</TouchableRipple> </TouchableRipple>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Checkbox <Checkbox
status={area.external ? 'checked' : 'unchecked'} status={area?.external ? 'checked' : 'unchecked'}
color={colors.blue} color={colors.blue}
uncheckedColor={colors.amethystSmoke} uncheckedColor={colors.amethystSmoke}
onPress={() => handleCheckboxArea({ name: 'external', checked: !area.external })} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.external })}
/> />
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.internal })}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.internal })}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
External (Diluar area/kawasan/parimeter Project) External (Diluar area/kawasan/parimeter Project)
</Text> </Text>
</TouchableRipple> </TouchableRipple>
</View> </View>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}>
Lokasi Pelaporan Lokasi Pelaporan
</Text> </Text>
<TouchableRipple onPress={handleProjectPress}> <TextInput
<TextInput dense={true}
dense={true} outlineColor={colors.amethystSmoke}
outlineColor={colors.amethystSmoke} activeOutlineColor={colors.blue}
activeOutlineColor={colors.blue} mode="outlined"
mode="outlined" label='Pilih Proyek'
label='Pilih Proyek' placeholder='Pilih Proyek'
placeholder='Pilih Proyek' value={reportLocation.projectLocation}
onChangeText={handleReportProjectLocationChange}
/> />
</TouchableRipple>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -155,8 +142,8 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Lokasi Laporan' label='Lokasi Laporan'
placeholder='Lokasi Laporan' placeholder='Lokasi Laporan'
value={reportLocation.reportLocation}
onChangeText={handleReportLocationChange}
/> />
<TextInput <TextInput
@ -167,11 +154,13 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Sub Lokasi Laporan' label='Sub Lokasi Laporan'
placeholder='Sub Lokasi Laporan' placeholder='Sub Lokasi Laporan'
value={reportLocation.subLocation}
onChangeText={handleReportSubLocationChange}
/> />
</View> </View>
</ScrollView> </ScrollView>
</> </>
) );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

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

@ -1,4 +1,4 @@
import React, { useRef, useCallback, useState, useMemo } from 'react'; import React, { useRef, useCallback, useState, useEffect } from 'react';
import { launchCamera, launchImageLibrary } from 'react-native-image-picker'; import { launchCamera, launchImageLibrary } from 'react-native-image-picker';
import { import {
BottomSheetModal, BottomSheetModal,
@ -6,29 +6,41 @@ import {
BottomSheetView BottomSheetView
} from '@gorhom/bottom-sheet'; } from '@gorhom/bottom-sheet';
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker'; import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker';
import { TextInput, IconButton, Button } from 'react-native-paper'; import { TextInput, IconButton, Button, useTheme } from 'react-native-paper';
import { StyleSheet, View, ScrollView, PermissionsAndroid, Image } from 'react-native'; import { StyleSheet, View, ScrollView, PermissionsAndroid, Image } from 'react-native';
import { colors } from '../../../utils/color'; import { useDispatch, useSelector } from 'react-redux';
import { strings } from '../../../utils/i18n'; import uuid from 'react-native-uuid';
import { requestAccessStoragePermission } from '../../../utils/storage'; import { setImages } from '../../../appredux/actions';
import { getCoords } from '../../../utils/geolocation'; import { getCoords } from '../../../utils/geolocation';
export default function MediaScreen() {
const [images, setImages] = useState([]);
export default function MediaScreen() {
const images = useSelector(state => state.incidentReportReducer.dataImage || []);
const theme = useTheme();
const isDarkTheme = theme.dark;
const dispatch = useDispatch();
const colorsheet = isDarkTheme ? theme.colors.black : theme.colors.pureWhite;
const [existingAttachmentNumber, setExistingAttachmentNumber] = useState('');
const bottomSheetModal = useRef(null); const bottomSheetModal = useRef(null);
console.log("images", useSelector(state => state.incidentReportReducer.dataImage));
const handleSetImageUri = (uri, description) => { const handleSetImageUri = (newImage) => {
const newImage = { uri: uri, description: description || "" }; const updatedImages = [...images, newImage];
setImages(prevImages => [...prevImages, newImage]); dispatch(setImages(updatedImages));
} };
const handleDeleteImage = (index) => { const handleDeleteImage = (index) => {
const updatedImages = [...images]; const updatedImages = [...images];
updatedImages.splice(index, 1); updatedImages.splice(index, 1);
setImages(updatedImages); dispatch(setImages(updatedImages));
}; };
useEffect(() => {
if (existingAttachmentNumber === '') {
setExistingAttachmentNumber(uuid.v4())
}
}, [])
const handleOpenSheet = useCallback(() => { const handleOpenSheet = useCallback(() => {
bottomSheetModal.current?.present(); bottomSheetModal.current?.present();
}, []); }, []);
@ -38,21 +50,21 @@ export default function MediaScreen() {
const granted = await PermissionsAndroid.request( const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA, PermissionsAndroid.PERMISSIONS.CAMERA,
{ {
title: strings('takePicture.cameraPermissionTitle'), title: 'Camera Permission',
message: strings('takePicture.cameraPermissionMessage'), message: 'App needs camera permission to take pictures.',
buttonNeutral: strings('takePicture.cameraPermissionBtnNeutral'), buttonNeutral: 'Ask Me Later',
buttonNegative: strings('takePicture.cameraPermissionBtnCancel'), buttonNegative: 'Cancel',
buttonPositive: strings('takePicture.cameraPermissionBtnPositive'), buttonPositive: 'OK',
}, },
); );
if (granted === PermissionsAndroid.RESULTS.GRANTED) { if (granted === PermissionsAndroid.RESULTS.GRANTED) {
handleLaunchCamera() handleLaunchCamera();
} else { } else {
console.log('Camera permission denied');
} }
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);
} }
bottomSheetModal.current?.dismiss();
}; };
const handleLaunchCamera = () => { const handleLaunchCamera = () => {
@ -61,93 +73,68 @@ export default function MediaScreen() {
skipBackup: true, skipBackup: true,
path: 'images', path: 'images',
}, },
cameraType: 'front', cameraType: 'back',
maxWidth: 768, maxWidth: 768,
maxHeight: 1024, maxHeight: 1024,
saveToPhotos: false, saveToPhotos: false,
includeBase64: true includeBase64: true
}; };
launchCamera(options, (response) => { launchCamera(options, async (response) => {
if (response.didCancel) { if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) { } else if (response.error) {
} else if (response.customButton) { console.log('ImagePicker Error: ', response.error);
} else { } else if (response.assets && response.assets.length > 0) {
if (response.assets && response.assets.length > 0) { try {
handleSetImageUri(response.assets[0].uri); getCoords(async (loc) => {
getCoords(loc => {
if (loc) { if (loc) {
let imageObject = { const timestamp = new Date().toLocaleString();
"image_uri": response.assets[0].uri, const location = { latitude: loc.lat, longitude: loc.lon };
"image_blob": response.assets[0].base64, const overlayText = `${timestamp}\nLatitude: ${location.latitude}\nLongitude: ${location.longitude}\nCopyright Nawakara`;
"lat": loc.lat, const markedImage = await ImageMarker.markText({
"lon": loc.lon backgroundImage: {
} src: { uri: response.assets[0].uri }
addOverlay(imageObject) },
watermarkTexts: [{
text: overlayText,
positionOptions: {
position: Position.bottomLeft,
},
style: {
color: '#ffffff',
fontSize: 9,
fontName: 'Arial',
textBackgroundStyle: {
padding: 12,
type: TextBackgroundType.none,
color: '#00000080'
}
},
}],
position: 'bottomLeft',
color: '#ffffff',
fontName: 'Arial-BoldMT',
fontSize: 16,
scale: 1,
});
const newImageData = {
id: 0,
attachment_number: existingAttachmentNumber,
file: `file://${markedImage}`,
data: response.assets[0].uri,
type: response.assets[0].type,
name: response.assets[0].fileName
};
handleSetImageUri(newImageData);
} }
}) });
} catch (error) {
console.error('Error adding overlay:', error);
} }
} }
}); });
} };
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/${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 = () => { const handleLaunchGallery = () => {
let options = { let options = {
@ -157,16 +144,26 @@ export default function MediaScreen() {
launchImageLibrary(options, (response) => { launchImageLibrary(options, (response) => {
if (response.didCancel) { if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) { } else if (response.error) {
} else if (response.customButton) { console.log('ImagePicker Error: ', response.error);
} else { } else {
handleSetImageUri(response.assets[0].uri); if (response.assets && response.assets.length > 0) {
const newImage = {
uri: response.assets[0].uri,
attachment_number: existingAttachmentNumber,
data: response.assets[0].uri,
type: response.assets[0].type,
name: response.assets[0].fileName
};
handleSetImageUri(newImage);
}
} }
}); });
bottomSheetModal.current?.dismiss(); bottomSheetModal.current?.dismiss();
} };
const renderImages = useMemo(() => images.map((image, index) => ( const renderImages = images.map((image, index) => (
<View key={index} style={styles.imageBlock}> <View key={index} style={styles.imageBlock}>
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
<Image <Image
@ -175,7 +172,6 @@ export default function MediaScreen() {
resizeMode="cover" resizeMode="cover"
/> />
<IconButton <IconButton
iconColor={colors.beanRed}
icon="delete" icon="delete"
style={styles.deleteButton} style={styles.deleteButton}
size={30} size={30}
@ -185,8 +181,8 @@ export default function MediaScreen() {
<TextInput <TextInput
mode='outlined' mode='outlined'
style={styles.descriptionInput} style={styles.descriptionInput}
outlineColor={colors.amethystSmoke} outlineColor="#CCC"
activeOutlineColor={colors.blue} activeOutlineColor="#000"
placeholder="Deskripsi gambar" placeholder="Deskripsi gambar"
label="Deskripsi gambar" label="Deskripsi gambar"
multiline={true} multiline={true}
@ -194,11 +190,11 @@ export default function MediaScreen() {
onChangeText={(text) => { onChangeText={(text) => {
const updatedImages = [...images]; const updatedImages = [...images];
updatedImages[index].description = text; updatedImages[index].description = text;
setImages(updatedImages); dispatch(setImages(updatedImages));
}} }}
/> />
</View> </View>
)), [images]); ));
return ( return (
<> <>
@ -207,7 +203,6 @@ export default function MediaScreen() {
style={styles.addButton} style={styles.addButton}
mode="contained-tonal" mode="contained-tonal"
onPress={handleOpenSheet} onPress={handleOpenSheet}
textColor={colors.blue}
> >
Tambah Gambar Tambah Gambar
</Button> </Button>
@ -243,7 +238,7 @@ export default function MediaScreen() {
</BottomSheetModal> </BottomSheetModal>
</BottomSheetModalProvider> </BottomSheetModalProvider>
</> </>
) );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -263,16 +258,15 @@ const styles = StyleSheet.create({
image: { image: {
width: 100, width: 100,
height: 100, height: 100,
borderRadius: 7 borderRadius: 7,
}, },
descriptionInput: { descriptionInput: {
flex: 1 flex: 1,
}, },
addButton: { addButton: {
marginHorizontal: 8, marginHorizontal: 8,
marginVertical: 10, marginVertical: 10,
borderRadius: 10, borderRadius: 10,
backgroundColor: colors.semiBlue,
}, },
bottomSheet: { bottomSheet: {
marginHorizontal: 15, marginHorizontal: 15,

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

@ -1,11 +1,40 @@
import React, { useState } from 'react'; import React from 'react';
import { TouchableRipple, TextInput, Card, Text } from 'react-native-paper'; import { TextInput, Card, Text, useTheme } from 'react-native-paper';
import { View, StyleSheet, ScrollView } from 'react-native'; import { View, StyleSheet, ScrollView } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker'; import { useDispatch, useSelector } from 'react-redux';
import moment from 'moment'; import { setDataRecepient, setDataReporter } from '../../../appredux/modules/incidentReport/actions';
import { colors } from '../../../utils/color'; import { colors } from '../../../utils/color';
export default function ReportScreen() { export default function ReportScreen() {
const theme = useTheme();
const isDarkTheme = theme.dark;
const dispatch = useDispatch();
const dataReporter = useSelector(state => state.incidentReportReducer.dataReporter);
const dataRecipient = useSelector(state => state.incidentReportReducer.dataRecipient);
const handleReporterNameChange = (name) => {
dispatch(setDataReporter({ name }));
};
const handleReporterPositionChange = (position) => {
dispatch(setDataReporter({ position }));
};
const handleReporterContactChange = (noContact) => {
dispatch(setDataReporter({ noContact }));
};
const handleRecipientNameChange = (name) => {
dispatch(setDataRecepient({ name }));
};
const handleRecipientPositionChange = (position) => {
dispatch(setDataRecepient({ position }));
};
const handleRecipientContactChange = (noContact) => {
dispatch(setDataRecepient({ noContact }));
};
return ( return (
<> <>
@ -21,77 +50,89 @@ export default function ReportScreen() {
</Card> </Card>
<ScrollView> <ScrollView>
<View style={styles.container}> <View style={styles.container}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6 }}>
Pelapor Pelapor
</Text> </Text>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label=" Nama Pelapor" label=" Nama Pelapor"
placeholder='Nama' placeholder='Nama'
value={dataReporter.name || ''}
onChangeText={handleReporterNameChange}
/> />
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='Posisi/Jabatan' label='Posisi/Jabatan'
placeholder='Posisi/Jabatan' placeholder='Posisi/Jabatan'
value={dataReporter.position || ''}
onChangeText={handleReporterPositionChange}
/> />
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='No Contact' label='No Contact'
placeholder='No Contact' placeholder='No Contact'
value={dataReporter.noContact || ''}
keyboardType='number-pad'
onChangeText={handleReporterContactChange}
/> />
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}>
Penerima Laporan Penerima Laporan
</Text> </Text>
<TextInput <TextInput
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='Nama' label='Nama'
placeholder='Nama' placeholder='Nama'
value={dataRecipient.name || ''}
onChangeText={handleRecipientNameChange}
/> />
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='Posisi/Jabatan' label='Posisi/Jabatan'
placeholder='Posisi/Jabatan' placeholder='Posisi/Jabatan'
value={dataRecipient.position || ''}
onChangeText={handleRecipientPositionChange}
/> />
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='No Contact' label='No Contact'
placeholder='No Contact' placeholder='No Contact'
value={dataRecipient.noContact || ''}
keyboardType='number-pad'
onChangeText={handleRecipientContactChange}
/> />
</View> </View>
</ScrollView> </ScrollView>
</> </>
) );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

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

@ -3,40 +3,49 @@ import { TouchableRipple, TextInput, Card, Text } from 'react-native-paper';
import { View, StyleSheet } from 'react-native'; import { View, StyleSheet } from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment'; import moment from 'moment';
import { useSelector, useDispatch } from 'react-redux';
import { setIncidentDate, setIncidentTime, setReportDate, setReportTime } from '../../../appredux/modules/incidentReport/actions';
import { colors } from '../../../utils/color'; import { colors } from '../../../utils/color';
export default function TimeScreen() { export default function TimeScreen() {
const [incidentDate, setIncidentDate] = useState(''); const dispatch = useDispatch();
const [incidentTime, setIncidentTime] = useState('');
const [reportDate, setReportDate] = useState(''); const incidentDate = useSelector(state => state.incidentReportReducer.incidentDate);
const [reportTime, setReportTime] = useState(''); const incidentTime = useSelector(state => state.incidentReportReducer.incidentTime);
const reportDate = useSelector(state => state.incidentReportReducer.reportDate);
const reportTime = useSelector(state => state.incidentReportReducer.reportTime);
const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false); const [showIncidentDatePicker, setShowIncidentDatePicker] = useState(false);
const [showIncidentTimePicker, setShowIncidentTimePicker] = useState(false); const [showIncidentTimePicker, setShowIncidentTimePicker] = useState(false);
const [showReportDatePicker, setShowReportDatePicker] = useState(false); const [showReportDatePicker, setShowReportDatePicker] = useState(false);
const [showReportTimePicker, setShowReportTimePicker] = useState(false); const [showReportTimePicker, setShowReportTimePicker] = useState(false);
console.log("incidentDate", incidentDate ? moment(incidentDate).format("YYYY-MM-DD HH:mm:ss Z") : 'null');
console.log("reportDate", reportDate ? moment(reportDate).format("YYYY-MM-DD HH:mm:ss Z") : 'null');
const handleIncidentDateChange = (event, selectedDate) => { const handleIncidentDateChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowIncidentDatePicker(false); setShowIncidentDatePicker(false);
setIncidentDate(currentDate); if (selectedDate) {
dispatch(setIncidentDate(selectedDate));
}
}; };
const handleIncidentTimeChange = (event, selectedDate) => { const handleIncidentTimeChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowIncidentTimePicker(false); setShowIncidentTimePicker(false);
setIncidentTime(currentDate); if (selectedDate) {
dispatch(setIncidentTime(selectedDate));
}
}; };
const handleReportDateChange = (event, selectedDate) => { const handleReportDateChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowReportDatePicker(false); setShowReportDatePicker(false);
setReportDate(currentDate); if (selectedDate) {
dispatch(setReportDate(selectedDate));
}
}; };
const handleReportTimeChange = (event, selectedDate) => { const handleReportTimeChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowReportTimePicker(false); setShowReportTimePicker(false);
setReportTime(currentDate); if (selectedDate) {
dispatch(setReportTime(selectedDate));
}
}; };
const showIncidentDatePickerFunc = () => { const showIncidentDatePickerFunc = () => {

Loading…
Cancel
Save