Compare commits

...

8 Commits

  1. 70
      index.js
  2. 18
      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. 190
      src/screens/incident-page/stepComponent/chronology.js
  15. 97
      src/screens/incident-page/stepComponent/containedAction.js
  16. 174
      src/screens/incident-page/stepComponent/incident.js
  17. 115
      src/screens/incident-page/stepComponent/location.js
  18. 158
      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. 37
      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,25 +1,69 @@
/** import React from 'react';
* @format import { AppRegistry, StatusBar, View, StyleSheet, useColorScheme } from 'react-native';
*/ import { Provider, useSelector } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { AppRegistry } from 'react-native'; import { PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-native-paper';
import App from './src/App'; import App from './src/App';
import { name as appName } from './app.json'; import { name as appName } from './app.json';
import { LogBox } from 'react-native'; import { persistor, store } from './src/appredux/store';
import { PaperProvider, useTheme } from 'react-native-paper';
const Main = () => {
const systemTheme = useColorScheme();
const { theme, useSystemTheme } = useSelector(state => state.themeReducer);
LogBox.ignoreLogs(['Warning: Failed prop type: Invalid prop `role`']); // Ignore log notification by message const customColors = {
LogBox.ignoreAllLogs(); 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',
};
const lightTheme = {
...MD3LightTheme,
colors: {
...MD3LightTheme.colors,
...customColors,
background: customColors.white,
text: customColors.black,
},
};
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 ( return (
<PaperProvider> <PaperProvider theme={appliedTheme}>
<App /> <App />
</PaperProvider> </PaperProvider>
); );
} };
const AppWrapper = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Main />
</PersistGate>
</Provider>
);
AppRegistry.registerComponent(appName, () => App); AppRegistry.registerComponent(appName, () => AppWrapper);

18
src/App.js

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

25
src/appredux/actions.js

@ -1,22 +1,7 @@
/* // user actions
* All actions should be import and exported first export * from './modules/user/actions';
* to make components / pages are easy to use
*/
import { // theme actions
setIsLogin, export * from './modules/theme/actions';
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

@ -0,0 +1,115 @@
// 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

@ -0,0 +1,89 @@
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 = obj => dispatch => { export const setChosenLanguage = (language) => dispatch => {
dispatch({ dispatch({
type: SET_CHOSEN_LANGUAGE, type: SET_CHOSEN_LANGUAGE,
payload: obj payload: language
}) });
} };
export const setRegister = obj => dispatch => { export const setRegister = obj => dispatch => {
dispatch({ dispatch({

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

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

6
src/appredux/reducers.js

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

68
src/config/ApiConst.js

@ -18,41 +18,49 @@ export const ERROR_LOGIN = "ERROR_LOGIN"
export const jenisKejadian = [ export const jenisKejadian = [
{ {
"data": [ "data": [
{ "label": "Vandalisme aset perusahaan", "level": "Critical", "checked": false }, { label: "Vandalisme aset perusahaan", "level": "Critical", "checked": false },
{ "label": "Perusakan aset", "level": "Critical", "checked": false }, { label: "Perusakan aset", "level": "Critical", "checked": false },
{ "label": "Pencurian", "level": "Critical", "checked": false }, { label: "Pencurian", "level": "Critical", "checked": false },
{ "label": "Penerobosan", "level": "Critical", "checked": false }, { label: "Penerobosan", "level": "Critical", "checked": false },
{ "label": "Kehilangan", "level": "Critical", "checked": false }, { label: "Kehilangan", "level": "Critical", "checked": false },
{ "label": "Peledakan", "level": "Critical", "checked": false }, { label: "Peledakan", "level": "Critical", "checked": false },
{ "label": "Pembakaran", "level": "Critical", "checked": false }, { label: "Pembakaran", "level": "Critical", "checked": false },
{ "label": "Pencemaran lingkungan", "level": "Critical", "checked": false }, { label: "Pencemaran lingkungan", "level": "Critical", "checked": false },
{ "label": "Perampokan", "level": "Critical", "checked": false }, { label: "Perampokan", "level": "Critical", "checked": false },
{ "label": "Pemalakan / Pemerasan", "level": "Critical", "checked": false }, { label: "Pemalakan / Pemerasan", "level": "Critical", "checked": false },
{ "label": "Kerusuhan sosial", "level": "Critical", "checked": false }, { label: "Kerusuhan sosial", "level": "Critical", "checked": false },
{ "label": "Pemogokan kerja", "level": "Critical", "checked": false }, { label: "Pemogokan kerja", "level": "Critical", "checked": false },
{ "label": "Keluhan User", "level": "Critical", "checked": false }, { label: "Keluhan User", "level": "Critical", "checked": false },
{ "label": "Serangan kekerasan", "level": "Critical", "checked": false }, { label: "Serangan kekerasan", "level": "Critical", "checked": false },
{ "label": "Gangguan masyarakat", "level": "Critical", "checked": false }, { label: "Gangguan masyarakat", "level": "Critical", "checked": false },
{ "label": "Sabotase", "level": "Critical", "checked": false }, { label: "Sabotase", "level": "Critical", "checked": false },
{ "label": "Narkoba", "level": "Critical", "checked": false }, { label: "Narkoba", "level": "Critical", "checked": false },
{ "label": "Minuman berakohol", "level": "Critical", "checked": false }, { label: "Minuman berakohol", "level": "Critical", "checked": false },
{ "label": "Perselisihan ketenagakerjaan", "level": "Critical", "checked": false }, { label: "Perselisihan ketenagakerjaan", "level": "Critical", "checked": false },
{ "label": "Tidak ada tindakan/respond", "level": "Critical", "checked": false }, { label: "Tidak ada tindakan/respond", "level": "Critical", "checked": false },
{ "label": "Pelanggaran SOP", "level": "Major", "checked": false }, { label: "Pelanggaran SOP", "level": "Major", "checked": false },
{ "label": "Indisipliner kerja anggota", "level": "Major", "checked": false }, { label: "Indisipliner kerja anggota", "level": "Major", "checked": false },
{ "label": "Kelengkapan seragam", "level": "Major", "checked": false }, { label: "Kelengkapan seragam", "level": "Major", "checked": false },
{ "label": "Kecelakaan kerja", "level": "Major", "checked": false }, { label: "Kecelakaan kerja", "level": "Major", "checked": false },
{ "label": "Kecelakaan Lalu Lintas", "level": "Major", "checked": false }, { label: "Kecelakaan Lalu Lintas", "level": "Major", "checked": false },
{ "label": "Kerusakan Kendaraan Project", "level": "Major", "checked": false }, { label: "Kerusakan Kendaraan Project", "level": "Major", "checked": false },
{ "label": "Kerusakan Equipment Project", "level": "Major", "checked": false }, { label: "Kerusakan Equipment Project", "level": "Major", "checked": false },
{ "label": "Keterlambatan Pembayaran Vendor", "level": "Major", "checked": false }, { label: "Keterlambatan Pembayaran Vendor", "level": "Major", "checked": false },
{ "label": "Keterlambatan Penggajian", "level": "Major", "checked": false }, { label: "Keterlambatan Penggajian", "level": "Major", "checked": false },
{ "label": "Lain-lain", "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 = [ export const dummyDataTraining = [
{ name: 'Gada Pratama', value: false, label: 'Gada Pratama' }, { name: 'Gada Pratama', value: false, label: 'Gada Pratama' },
{ name: 'Gada Madya', value: false, label: 'Gada Madya' }, { name: 'Gada Madya', value: false, label: 'Gada Madya' },

12
src/navigation/AppRoutes.js

@ -55,6 +55,8 @@ import DialogFormPost from '../screens/activity/projectAset/post/dialogForm';
import ProjectScreen from '../screens/activity/projectAset/project/index'; import ProjectScreen from '../screens/activity/projectAset/project/index';
import DialogFormProject from '../screens/activity/projectAset/project/dialogForm'; import DialogFormProject from '../screens/activity/projectAset/project/dialogForm';
import ProfileScreen from '../screens/Profile'; import ProfileScreen from '../screens/Profile';
import ThemeScreen from '../components/theme'
import LanguageScreen from '../components/language'
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
@ -319,6 +321,16 @@ const AppRoutes = () => {
name="ProfileScreen" name="ProfileScreen"
component={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() { export default function BottomTabNavigator() {
const { theme, useSystemTheme } = useSelector(state => state.themeReducer); 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; const activeColor = colors.blue;
return ( return (

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

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

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

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

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

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

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

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

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

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

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

@ -1,58 +1,44 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { TextInput, Card, Checkbox, Text, TouchableRipple } from 'react-native-paper'; import { TextInput, Card, Checkbox, Text, TouchableRipple, useTheme } from 'react-native-paper';
import { StyleSheet, View, ScrollView } from 'react-native'; import { StyleSheet, View, ScrollView } from 'react-native';
import { colors } from '../../../utils/color' import { colors } from '../../../utils/color'
import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux';
import { setArea, setIncidentLocation, setReportLocation } from '../../../appredux/modules/incidentReport/actions';
export default function LocationScreen() { export default function LocationScreen() {
const navigation = useNavigation(); const theme = useTheme();
const [selectedProject, setSelectedProject] = useState(null); const dispatch = useDispatch();
const [area, setArea] = useState({ external: false, internal: false }) const { user } = useSelector(state => state.userReducer);
const handleProjectPress = () => { const area = useSelector(state => state.incidentReportReducer.area);
navigation.navigate('SearchPage', { dummyData, onSelect: handleProjectSelect }); const incidentLocation = useSelector(state => state.incidentReportReducer.incidentLocation);
}; const reportLocation = useSelector(state => state.incidentReportReducer.reportLocation);
const isDarkTheme = theme.dark;
console.log("area", area);
const handleCheckboxArea = (data) => { const handleCheckboxArea = (data) => {
const { name } = data; const { name } = data;
setArea(prevState => ({ const updatedArea = { ...area, [name]: !area[name] };
...prevState, dispatch(setArea(updatedArea));
[name]: !prevState[name] };
}));
const handleIncidentLocationChange = (location) => {
dispatch(setIncidentLocation({ location }));
};
const handleIncidentSubLocationChange = (subLocation) => {
dispatch(setIncidentLocation({ subLocation }));
}; };
console.log("Area :", area);
const handleProjectSelect = (project) => { const handleReportProjectLocationChange = (projectLocation) => {
setSelectedProject(project); dispatch(setReportLocation({ projectLocation }));
}; };
const dummyData = [ const handleReportLocationChange = (reportLocation) => {
{ id: '1', name: 'MRT', icn: 'test1', sicn: 'test"1' }, dispatch(setReportLocation({ reportLocation }));
{ id: '2', name: 'PLN', icn: 'test1', sicn: 'test"1' }, };
{ id: '3', name: 'Pertamina', icn: 'test1', sicn: 'test"1' },
{ id: '4', name: 'MD Entertaiment', icn: 'test1', sicn: 'test"1' }, const handleReportSubLocationChange = (subLocation) => {
{ id: '5', name: 'Jhonny', icn: 'test1', sicn: 'test"1' }, dispatch(setReportLocation({ subLocation }));
{ id: '6', name: 'AIA', icn: 'test1', sicn: 'test"1' }, };
{ id: '7', name: 'PLN Pulogadung', icn: 'test1', sicn: 'test"1' },
{ id: '8', name: 'Google', icn: 'test1', sicn: 'test"1' },
{ id: '9', name: 'Amazon', icn: 'test1', sicn: 'test"1' },
{ id: '10', name: 'Microsoft', icn: 'test1', sicn: 'test"1' },
{ id: '11', name: 'Facebook', icn: 'test1', sicn: 'test"1' },
{ id: '12', name: 'Apple', icn: 'test1', sicn: 'test"1' },
{ id: '13', name: 'Tesla', icn: 'test1', sicn: 'test"1' },
{ id: '14', name: 'Netflix', icn: 'test1', sicn: 'test"1' },
{ id: '15', name: 'Twitter', icn: 'test1', sicn: 'test"1' },
{ id: '16', name: 'LinkedIn', icn: 'test1', sicn: 'test"1' },
{ id: '17', name: 'Instagram', icn: 'test1', sicn: 'test"1' },
{ id: '18', name: 'Snapchat', icn: 'test1', sicn: 'test"1' },
{ id: '19', name: 'WhatsApp', icn: 'test1', sicn: 'test"1' },
{ id: '20', name: 'Uber', icn: 'test1', sicn: 'test"1' },
{ id: '21', name: 'Airbnb', icn: 'test1', sicn: 'test"1' },
{ id: '22', name: 'Spotify', icn: 'test1', sicn: 'test"1' },
{ id: '23', name: 'Pinterest', icn: 'test1', sicn: 'test"1' },
{ id: '24', name: 'Reddit', icn: 'test1', sicn: 'test"1' },
{ id: '25', name: 'Dropbox', icn: 'test1', sicn: 'test"1' },
{ id: '26', name: 'Zoom', icn: 'test1', sicn: 'test"1' },
{ id: '27', name: 'Etsy' }
];
return ( return (
<> <>
@ -68,22 +54,20 @@ export default function LocationScreen() {
</Card> </Card>
<ScrollView> <ScrollView>
<View style={styles.container}> <View style={styles.container}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6 }}>
Lokasi Kejadian Lokasi Kejadian
</Text> </Text>
<TouchableRipple onPress={handleProjectPress}>
<TextInput <TextInput
dense={true} dense={true}
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
editable={false} editable={false}
label='Pilih Proyek' label='Proyek'
placeholder='Pilih Proyek' placeholder='Pilih Proyek'
value={selectedProject ? selectedProject.name : ''} value={user?.assigment_hr ? user?.assigment_hr?.join.project_charter_project_name : ''}
right={<TextInput.Icon icon="chevron-down" />}
/> />
</TouchableRipple>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -93,6 +77,8 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Lokasi Kejadian' label='Lokasi Kejadian'
placeholder='Lokasi Kejadian' placeholder='Lokasi Kejadian'
value={incidentLocation.location}
onChangeText={handleIncidentLocationChange}
/> />
<TextInput <TextInput
@ -103,39 +89,40 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Sub Lokasi Kejadian' label='Sub Lokasi Kejadian'
placeholder='Sub Lokasi Kejadian' placeholder='Sub Lokasi Kejadian'
value={incidentLocation.subLocation}
onChangeText={handleIncidentSubLocationChange}
/> />
<View style={styles.row}> <View style={styles.row}>
<Checkbox <Checkbox
status={area.internal ? 'checked' : 'unchecked'} status={area?.internal ? 'checked' : 'unchecked'}
color={colors.blue} color={colors.blue}
uncheckedColor={colors.amethystSmoke} uncheckedColor={colors.amethystSmoke}
onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}
/> />
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'internal', checked: !area.internal })}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
Internal (Didalam area/kawasan/parimeter Project) Internal (Didalam area/kawasan/parimeter Project)
</Text> </Text>
</TouchableRipple> </TouchableRipple>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Checkbox <Checkbox
status={area.external ? 'checked' : 'unchecked'} status={area?.external ? 'checked' : 'unchecked'}
color={colors.blue} color={colors.blue}
uncheckedColor={colors.amethystSmoke} uncheckedColor={colors.amethystSmoke}
onPress={() => handleCheckboxArea({ name: 'external', checked: !area.external })} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.external })}
/> />
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.internal })}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => handleCheckboxArea({ name: 'external', checked: !area.internal })}>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 14, marginBottom: 6, marginTop: 10 }}>
External (Diluar area/kawasan/parimeter Project) External (Diluar area/kawasan/parimeter Project)
</Text> </Text>
</TouchableRipple> </TouchableRipple>
</View> </View>
<Text variant="bodySmall" style={{ color: colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}> <Text variant="bodySmall" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontSize: 16, marginBottom: 6, marginTop: 10 }}>
Lokasi Pelaporan Lokasi Pelaporan
</Text> </Text>
<TouchableRipple onPress={handleProjectPress}>
<TextInput <TextInput
dense={true} dense={true}
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
@ -143,9 +130,9 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Pilih Proyek' label='Pilih Proyek'
placeholder='Pilih Proyek' placeholder='Pilih Proyek'
value={reportLocation.projectLocation}
onChangeText={handleReportProjectLocationChange}
/> />
</TouchableRipple>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -155,8 +142,8 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Lokasi Laporan' label='Lokasi Laporan'
placeholder='Lokasi Laporan' placeholder='Lokasi Laporan'
value={reportLocation.reportLocation}
onChangeText={handleReportLocationChange}
/> />
<TextInput <TextInput
@ -167,11 +154,13 @@ export default function LocationScreen() {
mode="outlined" mode="outlined"
label='Sub Lokasi Laporan' label='Sub Lokasi Laporan'
placeholder='Sub Lokasi Laporan' placeholder='Sub Lokasi Laporan'
value={reportLocation.subLocation}
onChangeText={handleReportSubLocationChange}
/> />
</View> </View>
</ScrollView> </ScrollView>
</> </>
) );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({

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

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

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

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

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

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

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.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('presence.attendanceHistory')} />
</Appbar.Header> </Appbar.Header>
<ScrollView <ScrollView
style={{ flex: 1, marginTop: 5, backgroundColor: theme.colors.surface }} style={{ flex: 1, marginTop: 5, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={refreshing} refreshing={refreshing}

37
src/screens/registerPage/index.js

@ -115,6 +115,12 @@ export default function DialogForm() {
}; };
try { try {
if (image && image.imageFile == null) {
Toast.show({
type: 'error',
text1: strings('global.imageNull'),
});
} else {
const resultImage = await handleUploadImage([image], PATH_ID); const resultImage = await handleUploadImage([image], PATH_ID);
const result = await requestAssign.addData(payload); const result = await requestAssign.addData(payload);
if (result.status === 201) { if (result.status === 201) {
@ -131,6 +137,7 @@ export default function DialogForm() {
text1: strings('presence.failedSendData'), text1: strings('presence.failedSendData'),
}); });
} }
}
} catch (error) { } catch (error) {
console.error("Network error sending presence data:", error); console.error("Network error sending presence data:", error);
Toast.show({ Toast.show({
@ -346,7 +353,6 @@ export default function DialogForm() {
// const tempPath = `file://${RNFS.TemporaryDirectoryPath}/prsensi/${moment().format('YYYYMMDDHHmmss')}.jpg`; // const tempPath = `file://${RNFS.TemporaryDirectoryPath}/prsensi/${moment().format('YYYYMMDDHHmmss')}.jpg`;
// await RNFS.copyFile(markedImage, tempPath); // await RNFS.copyFile(markedImage, tempPath);
console.log("markedImage", markedImage);
const newImageData = { const newImageData = {
id: 0, id: 0,
attachment_number: existingAttachmentNumber, attachment_number: existingAttachmentNumber,
@ -368,13 +374,18 @@ export default function DialogForm() {
}); });
}; };
const renderImage = useMemo(() => ( const renderImage = useMemo(() => {
if (!image?.imageFile) {
return null; // Don't render anything if imageFile is null or undefined
}
return (
<> <>
<View style={styles.imageBlock}> <View style={styles.imageBlock}>
<TouchableRipple onPress={() => handleOpenSheetImage(image?.imageFile)}> <TouchableRipple onPress={() => handleOpenSheetImage(image?.imageFile)}>
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
<Image <Image
source={{ uri: image?.imageFile }} source={{ uri: image.imageFile }}
style={styles.image} style={styles.image}
resizeMode="cover" resizeMode="cover"
/> />
@ -382,12 +393,19 @@ export default function DialogForm() {
</TouchableRipple> </TouchableRipple>
</View> </View>
<Button icon="delete" style={{ borderRadius: 10, backgroundColor: colors.semiRed, marginHorizontal: 5, marginBottom: 10 }} textColor={colors.beanRed} mode="contained-tonal" onPress={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')} {strings('global.delete')}
</Button> </Button>
</> </>
), [image, handleOpenSheetImage, handleDeleteImage]); );
}, [image, handleOpenSheetImage, handleDeleteImage]);
@ -451,7 +469,7 @@ export default function DialogForm() {
right={<TextInput.Icon icon="chevron-down" />} right={<TextInput.Icon icon="chevron-down" />}
/> />
</TouchableRipple> </TouchableRipple>
{position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheet}> <TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheet}>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
@ -466,6 +484,7 @@ export default function DialogForm() {
right={<TextInput.Icon icon="chevron-down" />} right={<TextInput.Icon icon="chevron-down" />}
/> />
</TouchableRipple> </TouchableRipple>
}
{position !== 'spv' && {position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetArea}> <TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetArea}>
<TextInput <TextInput
@ -511,11 +530,11 @@ export default function DialogForm() {
placeholder='Masa Berlaku KTA' placeholder='Masa Berlaku KTA'
/> />
</TouchableRipple> </TouchableRipple>
{image !== null && renderImage} {!image && image?.imageFile !== null && renderImage}
</ScrollView> </ScrollView>
<View style={{ width: '100%', marginTop: 10, marginBottom: 5 }}> <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}> <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')} {strings('global.addImage')} KTA
</Button> </Button>
</View> </View>
</View > </View >

2
src/utils/color.js

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

19
src/utils/i18n.js

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

38
src/utils/locales/en.json

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

32
src/utils/locales/id.json

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

Loading…
Cancel
Save