Compare commits

..

No commits in common. '7d4250654e0a36ea394dc66227f8eac33cc7847a' and '69d45cbcd8b39c5871d021a63620cef651d10bf8' have entirely different histories.

  1. 70
      index.js
  2. 24
      src/App.js
  3. 25
      src/appredux/actions.js
  4. 115
      src/appredux/modules/incidentReport/actions.js
  5. 89
      src/appredux/modules/incidentReport/reducers.js
  6. 8
      src/appredux/modules/user/actions.js
  7. 17
      src/appredux/modules/user/reducers.js
  8. 6
      src/appredux/reducers.js
  9. 68
      src/config/ApiConst.js
  10. 12
      src/navigation/AppRoutes.js
  11. 4
      src/navigation/BottomTabNavigator.js
  12. 138
      src/screens/incident-page/dialogForm.js
  13. 117
      src/screens/incident-page/index.js
  14. 228
      src/screens/incident-page/stepComponent/chronology.js
  15. 107
      src/screens/incident-page/stepComponent/containedAction.js
  16. 182
      src/screens/incident-page/stepComponent/incident.js
  17. 147
      src/screens/incident-page/stepComponent/location.js
  18. 208
      src/screens/incident-page/stepComponent/media.js
  19. 71
      src/screens/incident-page/stepComponent/report.js
  20. 35
      src/screens/incident-page/stepComponent/time.js
  21. 2
      src/screens/presence/index.js
  22. 115
      src/screens/registerPage/index.js
  23. 2
      src/utils/color.js
  24. 19
      src/utils/i18n.js
  25. 38
      src/utils/locales/en.json
  26. 32
      src/utils/locales/id.json

70
index.js

@ -1,69 +1,25 @@
import React from 'react';
import { AppRegistry, StatusBar, View, StyleSheet, useColorScheme } from 'react-native';
import { Provider, useSelector } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper';
/**
* @format
*/
import { AppRegistry } from 'react-native';
import App from './src/App';
import { name as appName } from './app.json';
import { persistor, store } from './src/appredux/store';
const Main = () => {
const systemTheme = useColorScheme();
const { theme, useSystemTheme } = useSelector(state => state.themeReducer);
const customColors = {
blue: '#3876BF',
semiBlue: '#DCECFF',
beanRed: '#EF6262',
semiRed: '#FFC5C3',
semiYellow: '#FFE7A3',
orange: '#FAA300',
green: '#17C13E',
semigreen: '#E3F8E8',
black: '#333333',
mistBlue: '#667085',
amethystSmoke: '#9A99AB',
mercury: '#E4E4E7',
catskillWhite: '#F2F4F6',
white: '#F9F9F9',
pureWhite: '#FFFFFF',
};
import { LogBox } from 'react-native';
import { PaperProvider, useTheme } from 'react-native-paper';
const lightTheme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
...customColors,
background: customColors.white,
text: customColors.black,
},
};
LogBox.ignoreLogs(['Warning: Failed prop type: Invalid prop `role`']); // Ignore log notification by message
LogBox.ignoreAllLogs();
const darkTheme = {
...MD3DarkTheme,
colors: {
...MD3DarkTheme.colors,
...customColors,
background: customColors.black,
text: customColors.white,
},
};
const appliedTheme = useSystemTheme ? (systemTheme === 'dark' ? darkTheme : lightTheme) : (theme === 'dark' ? darkTheme : lightTheme);
export default function Main() {
return (
<PaperProvider theme={appliedTheme}>
<PaperProvider>
<App />
</PaperProvider>
);
};
}
const AppWrapper = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Main />
</PersistGate>
</Provider>
);
AppRegistry.registerComponent(appName, () => AppWrapper);
AppRegistry.registerComponent(appName, () => App);

24
src/App.js

@ -1,24 +1,23 @@
import React from 'react';
import { StyleSheet, View, ActivityIndicator } from 'react-native';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './appredux/store';
import AppRoutes from './navigation/AppRoutes';
import { StyleSheet, View } from 'react-native';
import { ActivityIndicator } from 'react-native-paper';
import { persistor, store } from './appredux/store';
import AppRoutes from './navigation/AppRoutes'
import Toast from 'react-native-toast-message';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { colors } from '../src/utils/color';
import { colors } from '../src/utils/color'
const App = () => {
return (
<Provider store={store}>
<PersistGate
loading={
<View style={styles.container}>
<ActivityIndicator size='large' style={{ backgroundColor: colors.pureWhite }} animating={true} color={colors.blue} />
</View>
}
persistor={persistor}
>
<PersistGate loading={
<View style={styles.container}>
<ActivityIndicator size='large' style={{ backgroundColor: colors.pureWhite }} animating={true} color={colors.blue} />
</View>
} persistor={persistor}>
<GestureHandlerRootView style={{ flex: 1 }}>
<AppRoutes />
<Toast position='bottom' visibilityTime={5000} />
@ -28,6 +27,7 @@ const App = () => {
);
};
const styles = StyleSheet.create({
container: {
flex: 1,

25
src/appredux/actions.js

@ -1,7 +1,22 @@
// user actions
export * from './modules/user/actions';
/*
* All actions should be import and exported first
* to make components / pages are easy to use
*/
// theme actions
export * from './modules/theme/actions';
import {
setIsLogin,
setUser,
setFcmToken,
setChosenLanguage,
setRegister
} from './modules/user/actions';
export * from './modules/incidentReport/actions'
export {
setIsLogin,
setUser,
setFcmToken,
setChosenLanguage,
setRegister
}

115
src/appredux/modules/incidentReport/actions.js

@ -1,115 +0,0 @@
// Action Types
export const SET_INCIDENT_DATE = 'SET_INCIDENT_DATE';
export const SET_INCIDENT_TIME = 'SET_INCIDENT_TIME';
export const SET_REPORT_DATE = 'SET_REPORT_DATE';
export const SET_REPORT_TIME = 'SET_REPORT_TIME';
export const SET_AREA = 'SET_AREA';
export const SET_DATA_RECEPIENT = 'SET_DATA_RECEPIENT';
export const SET_DATA_REPORTER = 'SET_DATA_REPORTER';
export const SET_INCIDENT_LOCATION = 'SET_INCIDENT_LOCATION';
export const SET_REPORT_LOCATION = 'SET_REPORT_LOCATION';
export const SET_DATA_INCIDENT = 'SET_DATA_INCIDENT';
export const SET_DATA_ACHIEVEMENT = 'SET_DATA_ACHIEVEMENT';
export const SET_DATA_TYPE_RISK = 'SET_DATA_TYPE_RISK';
export const SET_INCIDENT_RISK_POTENTIAL = 'SET_INCIDENT_RISK_POTENTIAL';
export const SET_RISK_LEVEL = 'SET_RISK_LEVEL';
export const SET_ROOT_CASE = 'SET_ROOT_CASE';
export const SET_CHRONOLOGY = 'SET_CHRONOLOGY';
export const SET_DATA_PREVENTION = 'SET_DATA_PREVENTION';
export const SET_DATA_IMAGES = 'SET_DATA_IMAGES';
export const RESET_INCIDENT_REPORT = 'RESET_INCIDENT_REPORT';
// Action Creators
export const setIncidentDate = (date) => ({
type: SET_INCIDENT_DATE,
payload: date,
});
export const setIncidentTime = (time) => ({
type: SET_INCIDENT_TIME,
payload: time,
});
export const setReportDate = (date) => ({
type: SET_REPORT_DATE,
payload: date,
});
export const setReportTime = (time) => ({
type: SET_REPORT_TIME,
payload: time,
});
export const setArea = (area) => ({
type: SET_AREA,
payload: area,
});
export const setDataRecepient = (data) => ({
type: SET_DATA_RECEPIENT,
payload: data,
});
export const setDataReporter = (data) => ({
type: SET_DATA_REPORTER,
payload: data,
});
export const setIncidentLocation = (location) => ({
type: SET_INCIDENT_LOCATION,
payload: location,
});
export const setReportLocation = (location) => ({
type: SET_REPORT_LOCATION,
payload: location,
});
export const setDataIncident = (data) => ({
type: SET_DATA_INCIDENT,
payload: data,
});
export const setDataAchievement = (data) => ({
type: SET_DATA_ACHIEVEMENT,
payload: data,
});
export const setDataTypeRisk = (data) => ({
type: SET_DATA_TYPE_RISK,
payload: data,
});
export const setIncidentRiskPotential = (data) => ({
type: SET_INCIDENT_RISK_POTENTIAL,
payload: data,
});
export const setRiskLevel = (data) => ({
type: SET_RISK_LEVEL,
payload: data,
});
export const setChronology = (data) => ({
type: SET_CHRONOLOGY,
payload: data,
});
export const setRootCase = (data) => ({
type: SET_ROOT_CASE,
payload: data,
});
export const setDataPrevention = (data) => ({
type: SET_DATA_PREVENTION,
payload: data,
});
export const setImages = (images) => ({
type: SET_DATA_IMAGES,
payload: images,
});
export const resetIncidentReport = () => ({
type: RESET_INCIDENT_REPORT,
});

89
src/appredux/modules/incidentReport/reducers.js

@ -1,89 +0,0 @@
import {
SET_INCIDENT_DATE,
SET_INCIDENT_TIME,
SET_REPORT_DATE,
SET_REPORT_TIME,
SET_AREA,
SET_DATA_RECEPIENT,
SET_DATA_REPORTER,
SET_INCIDENT_LOCATION,
SET_REPORT_LOCATION,
SET_DATA_INCIDENT,
SET_DATA_ACHIEVEMENT,
SET_DATA_TYPE_RISK,
RESET_INCIDENT_REPORT,
SET_CHRONOLOGY,
SET_ROOT_CASE,
SET_INCIDENT_RISK_POTENTIAL,
SET_RISK_LEVEL,
SET_DATA_PREVENTION,
SET_DATA_IMAGES
} from './actions';
const initialState = {
area: { external: false, internal: false },
chronology: null,
dataAchievement: [],
dataIncidentReport: [],
dataPrevention: [],
dataRecipient: { name: "", noContact: "", position: "" },
dataReporter: { name: "", noContact: "", position: "" },
incidentDate: null,
incidentLocation: { location: "", subLocation: "" },
incidentRiskPotential: { impact: "", impactDescription: "", likelihood: "", likelihoodDescription: "", value: "" },
incidentTime: null,
dataImage: [],
reportDate: null,
reportLocation: { projectLocation: "", reportLocation: "", subLocation: "" },
reportTime: null,
riskLevel: { backgroundColor: "", color: "", level: "" },
rootCase: null,
typeRiskIncident: null,
};
export default function incidentReportReducer(state = initialState, action) {
switch (action.type) {
case SET_INCIDENT_DATE:
return { ...state, incidentDate: action.payload };
case SET_INCIDENT_TIME:
return { ...state, incidentTime: action.payload };
case SET_REPORT_DATE:
return { ...state, reportDate: action.payload };
case SET_REPORT_TIME:
return { ...state, reportTime: action.payload };
case SET_AREA:
return { ...state, area: action.payload };
case SET_DATA_RECEPIENT:
return { ...state, dataRecipient: { ...state.dataRecipient, ...action.payload } };
case SET_DATA_REPORTER:
return { ...state, dataReporter: { ...state.dataReporter, ...action.payload } };
case SET_INCIDENT_LOCATION:
return { ...state, incidentLocation: { ...state.incidentLocation, ...action.payload } };
case SET_REPORT_LOCATION:
return { ...state, reportLocation: { ...state.reportLocation, ...action.payload } };
case SET_DATA_INCIDENT:
return { ...state, dataIncidentReport: action.payload };
case SET_DATA_ACHIEVEMENT:
return { ...state, dataAchievement: action.payload };
case SET_DATA_TYPE_RISK:
return { ...state, typeRiskIncident: action.payload };
case SET_INCIDENT_RISK_POTENTIAL:
return { ...state, incidentRiskPotential: action.payload };
case SET_RISK_LEVEL:
return { ...state, riskLevel: action.payload };
case SET_CHRONOLOGY:
return { ...state, chronology: action.payload };
case SET_ROOT_CASE:
return { ...state, rootCase: action.payload };
case SET_DATA_PREVENTION:
return { ...state, dataPrevention: action.payload };
case SET_DATA_IMAGES:
return { ...state, dataImage: action.payload };
case RESET_INCIDENT_REPORT:
return initialState;
default:
return state;
}
}

8
src/appredux/modules/user/actions.js

@ -25,12 +25,12 @@ export const setFcmToken = obj => dispatch => {
})
}
export const setChosenLanguage = (language) => dispatch => {
export const setChosenLanguage = obj => dispatch => {
dispatch({
type: SET_CHOSEN_LANGUAGE,
payload: language
});
};
payload: obj
})
}
export const setRegister = obj => dispatch => {
dispatch({

17
src/appredux/modules/user/reducers.js

@ -6,26 +6,27 @@ import {
SET_REGISTER
} from "./actions";
const initialState = {
isLogin: false,
user: null,
user: null, // the payload after request login to API
fcmToken: null,
chosenLanguage: 'id',
chosenLanguage: 'en', // id / en
isRegister: false
};
}
function userReducer(state = initialState, action) {
switch (action.type) {
case SET_IS_LOGIN:
return { ...state, isLogin: action.payload };
return { ...state, isLogin: action.payload }
case SET_USER:
return { ...state, user: action.payload };
return { ...state, user: action.payload }
case SET_FCM_TOKEN:
return { ...state, fcmToken: action.payload };
return { ...state, fcmToken: action.payload }
case SET_CHOSEN_LANGUAGE:
return { ...state, chosenLanguage: action.payload };
return { ...state, chosenLanguage: action.payload }
case SET_REGISTER:
return { ...state, isRegister: action.payload };
return { ...state, isRegister: action.payload }
default:
return state;
}

6
src/appredux/reducers.js

@ -4,14 +4,10 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
// modules
import userReducer from './modules/user/reducers';
import themeReducer from './modules/theme/reducers';
import incidentReportReducer from './modules/incidentReport/reducers'
const rootReducer = combineReducers({
userReducer,
themeReducer,
incidentReportReducer
userReducer
});
const appReducer = (state, action) => {

68
src/config/ApiConst.js

@ -18,49 +18,41 @@ 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 }
{ "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 }
]
}
]
export const achievement = [
{ label: "Penghargaan", "checked": false },
{ label: "Penangkapan", "checked": false },
{ label: "Penemuan Barang", "checked": false },
{ label: "Penggagalan Pencurian", "checked": false },
{ label: "Lain-lain", "checked": false }
]
export const dummyDataTraining = [
{ name: 'Gada Pratama', value: false, label: 'Gada Pratama' },
{ name: 'Gada Madya', value: false, label: 'Gada Madya' },

12
src/navigation/AppRoutes.js

@ -55,8 +55,6 @@ import DialogFormPost from '../screens/activity/projectAset/post/dialogForm';
import ProjectScreen from '../screens/activity/projectAset/project/index';
import DialogFormProject from '../screens/activity/projectAset/project/dialogForm';
import ProfileScreen from '../screens/Profile';
import ThemeScreen from '../components/theme'
import LanguageScreen from '../components/language'
const Stack = createNativeStackNavigator();
@ -321,16 +319,6 @@ const AppRoutes = () => {
name="ProfileScreen"
component={ProfileScreen}
/>
<Stack.Screen
options={{ headerShown: false }}
name="ThemeScreen"
component={ThemeScreen}
/>
<Stack.Screen
options={{ headerShown: false }}
name="LanguageScreen"
component={LanguageScreen}
/>
</>
)
}

4
src/navigation/BottomTabNavigator.js

@ -16,9 +16,9 @@ const Tab = createBottomTabNavigator();
export default function BottomTabNavigator() {
const { theme, useSystemTheme } = useSelector(state => state.themeReducer);
const systemTheme = useColorScheme();
const tabBarColor = useSystemTheme ? (systemTheme === 'dark' ? colors.black : colors.pureWhite) : (theme === 'dark' ? colors.black : colors.pureWhite);
const tabBarColor = theme === 'dark' ? colors.black : colors.pureWhite;
const activeColor = colors.blue;
return (

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

@ -1,6 +1,6 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { BackHandler, StatusBar } from 'react-native';
import { Button, Dialog, Appbar, useTheme, Text, } from 'react-native-paper';
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';
@ -11,97 +11,10 @@ import ReportScreen from './stepComponent/report';
import IncidentScreen from './stepComponent/incident';
import ChronologyScreen from './stepComponent/chronology';
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 }) {
const theme = useTheme()
const dispatch = useDispatch()
const isDarkTheme = theme.dark
const [active, setActive] = useState(0);
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 labels = ["Waktu", "Lokasi", "Pelapor", "Kejadian", "Kronologis", "Media"];
const renderStepContent = useMemo(() => {
switch (active) {
@ -134,13 +47,13 @@ export default function DialogForm({ route, navigation }) {
}
};
return (
<View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<View style={styles.container}>
<StatusBar backgroundColor={colors.beanRed} barStyle="light-content" />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.beanRed }}>
<Appbar.BackAction color={colors.pureWhite} onPress={showDialog} />
<Appbar.BackAction color={colors.pureWhite} onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('incidentReport.title')} />
</Appbar.Header>
<View style={{ width: '100%', marginTop: 15, marginBottom: 2, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}>
<View style={{ width: '100%', marginTop: 15, marginBottom: 2, backgroundColor: colors.pureWhite }}>
<StepIndicator
stepCount={labels.length}
customStyles={stepIndicatorStyles}
@ -158,34 +71,17 @@ export default function DialogForm({ route, navigation }) {
</Button>
{active === labels.length - 1 ?
(
<Button mode="contained" textColor={isDarkTheme ? theme.colors.surface : theme.colors.pureWhite} style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={() => console.log('Handle Saved')}>
Simpan
</Button>
)
: (
<Button mode="contained" textColor={isDarkTheme ? theme.colors.surface : theme.colors.pureWhite} style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={handleNext}>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.beanRed }]} onPress={handleNext}>
Lanjut
</Button>
)}
</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 >
)
}
@ -194,6 +90,7 @@ const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
backgroundColor: colors.pureWhite
},
cardView: {
marginTop: 10,
@ -217,14 +114,7 @@ const styles = StyleSheet.create({
padding: 10,
paddingBottom: 20,
},
dialog: {
marginVertical: 20,
padding: 20,
marginHorizontal: 20,
borderRadius: 10,
justifyContent: "center",
alignItems: "center"
}
})
const stepIndicatorStyles = {
@ -243,12 +133,12 @@ const stepIndicatorStyles = {
stepIndicatorFinishedColor: colors.beanRed,
stepIndicatorUnFinishedColor: colors.semiRed,
stepIndicatorCurrentColor: colors.beanRed,
stepIndicatorLabelFontSize: 11,
currentStepIndicatorLabelFontSize: 11,
stepIndicatorLabelFontSize: 12,
currentStepIndicatorLabelFontSize: 12,
stepIndicatorLabelCurrentColor: colors.pureWhite,
stepIndicatorLabelFinishedColor: colors.pureWhite,
stepIndicatorLabelUnFinishedColor: colors.beanRed,
labelColor: colors.semiRed,
labelSize: 11,
labelSize: 12,
currentStepLabelColor: colors.beanRed
}

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

@ -4,77 +4,24 @@ import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions, Status
import { colors } from '../../utils/color';
import Icon from 'react-native-vector-icons/AntDesign';
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 }) {
const theme = useTheme();
const isDarkTheme = theme.dark;
const { user } = useSelector(state => state.userReducer);
const request = new RequestModule('incident-report');
const [refreshing, setRefreshing] = useState(false);
const [page, setPage] = useState(0);
const [dataIncident, setDataIncident] = useState([]);
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 });
};
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 (
<View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.pureWhite} barStyle={isDarkTheme ? 'light-content' : 'dark-content'} translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite, elevation: 4 }}>
<View style={styles.container}>
<StatusBar backgroundColor={colors.white} barStyle='dark-content' translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.pureWhite, elevation: 4 }}>
<Appbar.BackAction onPress={() => { navigation.goBack() }} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} title={strings('incidentReport.title')} />
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} title={strings('incidentReport.title')} />
</Appbar.Header>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
@ -82,34 +29,23 @@ export default function IncidentScreen({ route, navigation }) {
{strings('incidentReport.add')}
</Button>
</View>
<ScrollView
style={{ flex: 1, marginTop: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}
refreshControl={
<RefreshControl
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 }]}>
<ScrollView style={{ backgroundColor: colors.pureWhite }}>
{incidentData.map((incident) => (
<TouchableRipple key={incident.id} onPress={() => { navigation.navigate('DialogFormIncident') }}>
<Card key={incident.id} elevation={2} style={styles.card}>
<View style={styles.cardContent}>
<View style={styles.leftContent}>
<Icon name="warning" size={25} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.blue} />
<Icon name="warning" size={25} color={colors.blue} />
</View>
<View style={styles.midContent}>
<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, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{incident.join.project_charter_project_name}</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, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.blue }]}>{incident.join.project_charter_icn} - {incident.join.project_charter_sicn}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.date}</Text>
<Text variant="headlineMedium" style={styles.Text}>{incident.location}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.company}</Text>
<Text variant="bodySmall" style={styles.subText}>{incident.icn} - {incident.sicn}</Text>
</View>
<View style={styles.rightContent}>
<Icon name="right" size={25} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.blue} />
<Icon name="right" size={25} color={colors.blue} />
</View>
</View>
</Card>
@ -124,19 +60,22 @@ const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
backgroundColor: colors.pureWhite
},
Text: {
fontSize: 16,
fontWeight: 'bold',
color: colors.blue
},
card: {
flex: 1,
marginHorizontal: 8,
marginVertical: 5,
backgroundColor: colors.pureWhite,
},
subText: {
fontSize: 12,
color: colors.blue
},
cardContent: {
flexDirection: 'row',

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

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

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

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

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

@ -1,193 +1,63 @@
import React, { useCallback, useRef, useState } from 'react';
import { TouchableRipple, Chip, Card, Text, useTheme, Searchbar, List, Button } from 'react-native-paper';
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'
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() {
const theme = useTheme();
const isDarkTheme = theme.dark;
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 [selectedItems, setSelectedItems] = useState([]);
const [dataIncidentReport, setDataIncidentReport] = useState(jenisKejadian[0].data)
const [typeRiskIncident, setTypeRiskIncident] = useState("")
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 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);
dispatch(setDataTypeRisk(updatedDataIncidentReport.some(item => item.checked) ? (hasCritical ? 'Critical' : 'Major') : ''));
dispatch(setDataIncident(updatedDataIncidentReport));
setFilter('');
bottomSheetIncindentModal.current?.dismiss();
}
setTypeRiskIncident(updatedDataIncidentReport.some(item => item.checked) ? (hasCritical ? 'Critical' : 'Major') : '');
};
const dataFilterIncident = dataIncidentReport.filter(item => item.label.toLowerCase().includes(filter.toLowerCase()));
const dataFilterAchievement = dataAchievement.filter(item => item.label.toLowerCase().includes(filter.toLowerCase()));
const renderAchievement = (data) => (
<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>
);
const isItemSelected = (label) => {
return dataIncidentReport.some(item => item.label === label && item.checked);
};
return (
<>
<Card style={{ backgroundColor: colors.semiRed, paddingVertical: 15, paddingHorizontal: 15, marginHorizontal: 8, marginVertical: 5, borderRadius: 5 }}>
<View>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontWeight: 'bold', fontSize: 16 }}>
{strings('incidentReport.achievement')} & {strings('incidentReport.incident')}
Jenis kejadian
</Text>
<Text variant="bodySmall" style={{ color: colors.beanRed, fontSize: 12 }}>
Capaian/Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu
Kejadian apa saja yang terjadi di lokasi, bisa pilih lebih dari satu
</Text>
</View>
</Card>
<View style={styles.container}>
<Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16 }}>
{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 variant="bodySmall" style={{ color: colors.black, fontSize: 16 }}>
Pilih Jenis Kejadian
</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}>
<View>
{dataIncidentReport.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={() => handleSelectIncident(item)} style={{ backgroundColor: colors.semiBlue, marginBottom: 5 }}>{item.label}</Chip>
{dataIncidentReport.map(item => (
<>
<Chip
icon={() => (
<Icon name={isItemSelected(item.label) ? 'circle-slice-8' : 'circle-outline'} size={20} color={isItemSelected(item.label) ? colors.pureWhite : colors.blue} />
)} textStyle={{ color: isItemSelected(item.label) ? colors.pureWhite : colors.blue }} onPress={() => handleCheckboxChange(item)} style={{ backgroundColor: isItemSelected(item.label) ? colors.beanRed : colors.semiBlue, marginBottom: 5 }}>{item.label}</Chip >
</>
))}
</View>
</ScrollView>
</View>
<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>
</ScrollView >
</View >
</>
)
}
@ -201,5 +71,5 @@ const styles = StyleSheet.create({
listData: {
flex: 1,
marginTop: 10,
}
},
});

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

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

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

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

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

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

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

@ -3,49 +3,40 @@ 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 { useSelector, useDispatch } from 'react-redux';
import { setIncidentDate, setIncidentTime, setReportDate, setReportTime } from '../../../appredux/modules/incidentReport/actions';
import { colors } from '../../../utils/color';
export default function TimeScreen() {
const dispatch = useDispatch();
const incidentDate = useSelector(state => state.incidentReportReducer.incidentDate);
const incidentTime = useSelector(state => state.incidentReportReducer.incidentTime);
const reportDate = useSelector(state => state.incidentReportReducer.reportDate);
const reportTime = useSelector(state => state.incidentReportReducer.reportTime);
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);
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 currentDate = selectedDate;
setShowIncidentDatePicker(false);
if (selectedDate) {
dispatch(setIncidentDate(selectedDate));
}
setIncidentDate(currentDate);
};
const handleIncidentTimeChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowIncidentTimePicker(false);
if (selectedDate) {
dispatch(setIncidentTime(selectedDate));
}
setIncidentTime(currentDate);
};
const handleReportDateChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowReportDatePicker(false);
if (selectedDate) {
dispatch(setReportDate(selectedDate));
}
setReportDate(currentDate);
};
const handleReportTimeChange = (event, selectedDate) => {
const currentDate = selectedDate;
setShowReportTimePicker(false);
if (selectedDate) {
dispatch(setReportTime(selectedDate));
}
setReportTime(currentDate);
};
const showIncidentDatePickerFunc = () => {

2
src/screens/presence/index.js

@ -109,7 +109,7 @@ export default function PresenceScreen({ route, navigation }) {
<Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('presence.attendanceHistory')} />
</Appbar.Header>
<ScrollView
style={{ flex: 1, marginTop: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}
style={{ flex: 1, marginTop: 5, backgroundColor: theme.colors.surface }}
refreshControl={
<RefreshControl
refreshing={refreshing}

115
src/screens/registerPage/index.js

@ -115,28 +115,21 @@ export default function DialogForm() {
};
try {
if (image && image.imageFile == null) {
const resultImage = await handleUploadImage([image], PATH_ID);
const result = await requestAssign.addData(payload);
if (result.status === 201) {
store.dispatch(setRegister(true));
Toast.show({
type: 'error',
text1: strings('global.imageNull'),
type: 'success',
text1: strings('register.dataSentSuccessfully'),
});
navigation.navigate('App');
clearForm()
} else {
const resultImage = await handleUploadImage([image], PATH_ID);
const result = await requestAssign.addData(payload);
if (result.status === 201) {
store.dispatch(setRegister(true));
Toast.show({
type: 'success',
text1: strings('register.dataSentSuccessfully'),
});
navigation.navigate('App');
clearForm()
} else {
Toast.show({
type: 'error',
text1: strings('presence.failedSendData'),
});
}
Toast.show({
type: 'error',
text1: strings('presence.failedSendData'),
});
}
} catch (error) {
console.error("Network error sending presence data:", error);
@ -353,6 +346,7 @@ export default function DialogForm() {
// const tempPath = `file://${RNFS.TemporaryDirectoryPath}/prsensi/${moment().format('YYYYMMDDHHmmss')}.jpg`;
// await RNFS.copyFile(markedImage, tempPath);
console.log("markedImage", markedImage);
const newImageData = {
id: 0,
attachment_number: existingAttachmentNumber,
@ -374,38 +368,26 @@ export default function DialogForm() {
});
};
const renderImage = useMemo(() => {
if (!image?.imageFile) {
return null; // Don't render anything if imageFile is null or undefined
}
return (
<>
<View style={styles.imageBlock}>
<TouchableRipple onPress={() => handleOpenSheetImage(image?.imageFile)}>
<View style={styles.imageContainer}>
<Image
source={{ uri: image.imageFile }}
style={styles.image}
resizeMode="cover"
/>
</View>
</TouchableRipple>
</View>
const renderImage = useMemo(() => (
<>
<View style={styles.imageBlock}>
<TouchableRipple onPress={() => handleOpenSheetImage(image?.imageFile)}>
<View style={styles.imageContainer}>
<Image
source={{ uri: image?.imageFile }}
style={styles.image}
resizeMode="cover"
/>
</View>
</TouchableRipple>
</View>
<Button
icon="delete"
style={{ borderRadius: 10, backgroundColor: colors.semiRed, marginHorizontal: 5, marginBottom: 10 }}
textColor={colors.beanRed}
mode="contained-tonal"
onPress={handleDeleteImage}
>
{strings('global.delete')}
</Button>
</>
);
}, [image, handleOpenSheetImage, handleDeleteImage]);
<Button icon="delete" style={{ borderRadius: 10, backgroundColor: colors.semiRed, marginHorizontal: 5, marginBottom: 10 }} textColor={colors.beanRed} mode="contained-tonal" onPress={handleDeleteImage}>
{strings('global.delete')}
</Button>
</>
), [image, handleOpenSheetImage, handleDeleteImage]);
@ -469,22 +451,21 @@ export default function DialogForm() {
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
{position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheet}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={shift ? shift : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Shift'
placeholder='Shift'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
}
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheet}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={shift ? shift : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Shift'
placeholder='Shift'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
{position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetArea}>
<TextInput
@ -530,11 +511,11 @@ export default function DialogForm() {
placeholder='Masa Berlaku KTA'
/>
</TouchableRipple>
{!image && image?.imageFile !== null && renderImage}
{image !== null && renderImage}
</ScrollView>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}>
<Button icon="camera-plus" style={{ borderRadius: 10, backgroundColor: colors.semiBlue, paddingVertical: 5, marginHorizontal: 5, }} textColor={colors.blue} mode="contained-tonal" onPress={handleTakePicture}>
{strings('global.addImage')} KTA
{strings('global.addImage')}
</Button>
</View>
</View >

2
src/utils/color.js

@ -14,6 +14,4 @@ export const colors = {
catskillWhite: '#F2F4F6',
white: '#F9F9F9',
pureWhite: '#fFFFFF',
yellow: "#F8DC49",
red: "#AE110C"
};

19
src/utils/i18n.js

@ -1,14 +1,13 @@
import { I18n } from "i18n-js";
import * as RNLocalize from "react-native-localize";
import en from "./locales/en";
import id from "./locales/id";
import LocalizedStrings from "react-native-localization";
const locales = RNLocalize.getLocales();
const i18n = new I18n({
en,
id,
});
const i18n = new I18n({ en, id });
if (Array.isArray(locales)) {
i18n.locale = locales[0].languageCode;
@ -18,9 +17,13 @@ i18n.fallbacks = true;
export const strings = (name, params = {}) => {
return i18n.t(name, params);
};
}
const localStr = new LocalizedStrings({
en,
id
});
export const changeLanguage = (languageKey) => {
i18n.locale = languageKey;
console.log("languageKey", languageKey);
};
localStr.setLanguage(languageKey)
}

38
src/utils/locales/en.json

@ -25,16 +25,13 @@
"send": "Send",
"sending": "Sending",
"errorConnectionMsg": "Failed to connect internet",
"backMessage": "Are you sure you want to back ?",
"areYouSure": "Are you sure?",
"noData": "No data found",
"register": "Register",
"fill": "FILL",
"press": "PRESS",
"search": "Search",
"panicButton": "PANIC BUTTON",
"panicButtonMessage": "Press this button in case of an incident",
"message": "Data inputted will be lost",
"imageNull": "Image cannot be null"
"panicButtonMessage": "Press this button in case of an incident"
},
"profile": {
"profile": "Profile",
@ -47,15 +44,13 @@
"signout": "Sign Out",
"signoutMessage": "Are you sure you want to log out of this account?",
"about": "About",
"languageSetting": "Language Setting",
"changeTheme": "Theme Setting",
"personnelList": "Personel List"
"languageSetting": "Language Setting"
},
"loginPage": {
"headMessage": "Welcome back!",
"signInBtn": "SIGN IN",
"passwordErrorMsg": "Pleas fill the password",
"successLogin": "welcome to Neops"
"login": {
"welcomeMessage": "Welcome back!",
"scanuserid": "Scan User ID",
"userlogin": "Login User",
"usernamenotexist": "Username does not exist!"
},
"home": {
"welcomeMessage": "Welcome"
@ -86,14 +81,7 @@
"incidentReport": {
"title": "Incident Report",
"add": "Add Incident Report",
"containedAction": "Add Contained Action",
"incidentSelect": "Select Incident",
"achievementSelect": "Select Achievement",
"incidentStep": "Capaian & Kejadian",
"timeStep": "Time",
"locationStep": "Location",
"reportStep": "Reporter",
"chronologyStep": "Chronology"
"containedAction": "Add Contained Action"
},
"patrol": {
"title": "Patrol",
@ -108,13 +96,7 @@
"failedSendDataPresence": "Failed to send presence data",
"presenceOutMessage": "Thank you for your dedication and work today",
"dataSentSuccessfully": "Presence Data Successfully Saved",
"dataSentOutSuccessfully": "Out attendance data is successfully saved",
"attendanceHistory": "Presence History"
},
"register": {
"title": "Registration",
"dataSentSuccessfully": "Register Data Successfully Saved",
"failedSendData": "Failed to send register data"
"dataSentOutSuccessfully": "Out attendance data is successfully saved"
},
"activity": {
"title": "Report Activity",

32
src/utils/locales/id.json

@ -24,25 +24,22 @@
"close": "Tutup",
"send": "Kirim",
"sending": "Mengirim",
"backMessage": "Apakah anda ingin kembali ?",
"errorConnectionMsg": "Gagal menghubungkan ke internet",
"reject": "Tolak",
"areYouSure": "Apakah anda yakin?",
"noData": "Tidak ada data ditemukan",
"register": "Pendaftaran",
"fill": "ISI",
"press": "TEKAN",
"search": "Cari",
"panicButton": "TOMBOL PANIK",
"panicButtonMessage": "Press this button in case of an incident",
"message": "Data yang di input akan hilang",
"imageNull": "Gambar Tidak Boleh Kosong"
"panicButtonMessage": "Press this button in case of an incident"
},
"loginPage": {
"headMessage": "Selamat datang!",
"signInBtn": "MASUK",
"usernameErrorMsg": "Username harus di antara 3-30 karakter",
"passwordErrorMsg": "Password tidak boleh kosong",
"successLogin": "Selamat Datang di Neops"
"successLogin": "Berhasil login aplikasi"
},
"profile": {
"profile": "Profil",
@ -55,9 +52,7 @@
"signout": "Keluar Aplikasi",
"signoutMessage": "Apakah anda yakin ingin keluar dari akun ini?",
"about": "Tentang",
"languageSetting": "Pengaturan Bahasa",
"changeTheme": "Ganti Tampilan",
"personnelList": "Daftar Personel"
"languageSetting": "Pengaturan Bahasa"
},
"home": {
"welcomeMessage": "Selamat datang",
@ -72,13 +67,7 @@
"failedSendDataPresence": "Gagal mengirim data kehadiran",
"errorFetchingLocation": "Gagal Mendapatkan Titik Lokasi",
"dataSentSuccessfully": "Data Presensi Berhasil Disimpan",
"dataSentOutSuccessfully": "Data Presensi Keluar Berhasil Disimpan",
"attendanceHistory": "Riwayat Kehadiran"
},
"register": {
"title": "Pendaftaran",
"dataSentSuccessfully": "Data Pendaftaran Berhasil Disimpan",
"failedSendData": "Gagal mengirim data pendaftaran"
"dataSentOutSuccessfully": "Data Presensi Keluar Berhasil Disimpan"
},
"takePicture": {
"select": "Pilih",
@ -106,16 +95,7 @@
"incidentReport": {
"title": "Laporan Kejadian",
"add": "Tambah Laporan",
"containedAction": "Tambah Rencana Pencegahan",
"incidentSelect": "Pilih Kejadian",
"achievementSelect": "Pilih Capaian",
"incident": "Kejadian",
"achievement": "Capaian",
"incidentStep": "Capaian & Kejadian",
"timeStep": "Waktu",
"locationStep": "Lokasi",
"reportStep": "Pelapor",
"chronologyStep": "Kronologis"
"containedAction": "Tambah Rencana Pencegahan"
},
"patrol": {
"title": "Patroli",

Loading…
Cancel
Save