Browse Source

feat(dark mode): add dark mode

master
farhantock 3 months ago
parent
commit
ffd2acf3f2
  1. 13
      src/components/SearchPage.js
  2. 93
      src/components/renderSkeleton.js
  3. 19
      src/navigation/BottomTabNavigator.js
  4. 53
      src/screens/Home.js
  5. 87
      src/screens/Login.js
  6. 154
      src/screens/Profile.js
  7. 13
      src/screens/Service.js
  8. 80
      src/screens/presence/index.js
  9. 248
      src/screens/registerPage/index.js

13
src/components/SearchPage.js

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import { List, Searchbar, TouchableRipple } from 'react-native-paper'; import { List, Searchbar, TouchableRipple, useTheme } from 'react-native-paper';
import { StyleSheet, View, ScrollView } from 'react-native'; import { StyleSheet, View, ScrollView } from 'react-native';
import { useRoute } from '@react-navigation/native'; import { useRoute } from '@react-navigation/native';
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';
export default function SearchPage({ navigation }) { export default function SearchPage({ navigation }) {
const theme = useTheme();
const [searchQuery, setSearchQuery] = React.useState(''); const [searchQuery, setSearchQuery] = React.useState('');
const route = useRoute(); const route = useRoute();
const { dataListProjectCharters, onSelect } = route.params; const { dataListProjectCharters, onSelect } = route.params;
@ -20,21 +21,21 @@ export default function SearchPage({ navigation }) {
); );
return ( return (
<View style={styles.container}> <View style={[styles.container, { backgroundColor: theme.colors.background }]}>
<Searchbar <Searchbar
placeholder="Cari Project" placeholder="Cari Project"
onChangeText={setSearchQuery} onChangeText={setSearchQuery}
value={searchQuery} value={searchQuery}
style={{ backgroundColor: colors.white }} style={{ backgroundColor: theme.dark ? theme.colors.surface : theme.colors.pureWhite, marginTop: 50, marginHorizontal: 10 }}
/> />
<ScrollView style={styles.listData}> <ScrollView style={[styles.listData, { backgroundColor: theme.colors.background }]}>
<View> <View>
{filteredData.map(item => ( {filteredData.map(item => (
<TouchableRipple key={item.id} onPress={() => handleProjectSelect(item)}> <TouchableRipple key={item.id} onPress={() => handleProjectSelect(item)}>
<List.Item <List.Item
title={item.sicn} title={item.sicn}
titleNumberOfLines={3} titleNumberOfLines={3}
left={() => <Icon name="search1" size={20} color={colors.black} />} left={() => <Icon name="search1" size={20} color={theme.dark ? theme.colors.surface : theme.colors.black} />}
right={() => <List.Icon icon="arrow-top-left" />} right={() => <List.Icon icon="arrow-top-left" />}
/> />
</TouchableRipple> </TouchableRipple>
@ -48,8 +49,6 @@ export default function SearchPage({ navigation }) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 50,
marginHorizontal: 10,
}, },
listData: { listData: {
flex: 1, flex: 1,

93
src/components/renderSkeleton.js

@ -2,49 +2,72 @@ import React from 'react';
import { View, StyleSheet } from 'react-native'; import { View, StyleSheet } from 'react-native';
import { Card } from 'react-native-paper'; import { Card } from 'react-native-paper';
import SkeletonPlaceholder from 'react-native-skeleton-placeholder'; import SkeletonPlaceholder from 'react-native-skeleton-placeholder';
import { colors } from '../utils/color';
const renderSkeletonPresence = (length) => { const renderSkeletonPresence = (length, theme) => {
return Array.from({ length }).map((_, index) => ( return (
<Card key={index} style={styles.cardPrecense}> <View>
<Card.Content> {Array.from({ length }).map((_, index) => (
<SkeletonPlaceholder> <Card key={index} style={[styles.cardPresence, { backgroundColor: theme.dark ? theme.colors.black : theme.colors.pureWhite }]}>
<View style={[styles.row, { justifyContent: 'space-between' }]}> <Card.Content>
<View style={[styles.row, styles.presenceDate, { width: 150, height: 20, borderRadius: 5 }]} /> <SkeletonPlaceholder
<View style={[styles.presenceDate, { backgroundColor: '#E0E0E0', width: 80, height: 20, borderRadius: 5 }]} /> borderRadius={5}
</View> backgroundColor={theme.dark ? theme.colors.amnestySmoke : theme.colors.amnestySmoke}
<View style={[styles.row, { justifyContent: 'space-between' }]}> highlightColor={theme.dark ? theme.colors.mistBlue : theme.colors.semiBlue}
<View style={[styles.row, { width: 150, height: 20 }]}> >
<View style={{ width: 20, height: 20, borderRadius: 5 }} /> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={{ width: 100, height: 20, marginLeft: 5, borderRadius: 5 }} /> <View style={[styles.row, styles.presenceDate]}>
</View> <SkeletonPlaceholder.Item width={20} marginRight={2} height={20} />
<View style={[styles.row, { width: 150, height: 20 }]}> <SkeletonPlaceholder.Item width={110} height={20} />
<View style={{ width: 40, height: 20, borderRadius: 5 }} /> </View>
<View style={{ width: 40, height: 20, marginLeft: 5, borderRadius: 5 }} /> <View style={[styles.presenceDate, { backgroundColor: colors.semigreen }]}>
<View style={{ width: 40, height: 20, marginLeft: 5, borderRadius: 5 }} /> <SkeletonPlaceholder.Item width={100} height={20} />
</View> </View>
</View> </View>
<View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={{ width: 150, height: 20, borderRadius: 5 }} /> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={{ width: 150, height: 20, marginLeft: 5, borderRadius: 5 }} /> <View style={styles.row}>
</View> <SkeletonPlaceholder.Item width={20} marginRight={2} height={20} />
</SkeletonPlaceholder> <SkeletonPlaceholder.Item width={100} height={20} />
</Card.Content> </View>
</Card> <View style={styles.row}>
)); <SkeletonPlaceholder.Item width={40} height={20} />
<SkeletonPlaceholder.Item width={20} marginHorizontal={3} height={20} />
<SkeletonPlaceholder.Item width={40} height={20} />
</View>
</View>
<View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={styles.row}>
<SkeletonPlaceholder.Item width={100} height={20} />
</View>
<View style={styles.row}>
<SkeletonPlaceholder.Item width={40} height={20} />
<SkeletonPlaceholder.Item width={20} marginHorizontal={3} height={20} />
<SkeletonPlaceholder.Item width={40} height={20} />
</View>
</View>
</SkeletonPlaceholder>
</Card.Content>
</Card>
))}
</View>
);
}; };
const styles = StyleSheet.create({ const styles = StyleSheet.create({
presenceDate: {
borderRadius: 10,
},
row: { row: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginBottom: 5,
}, },
cardPrecense: {
margin: 10, cardPresence: {
padding: 10, marginHorizontal: 10,
}, marginVertical: 2
presenceDate: {
padding: 5,
borderRadius: 5,
}, },
}); });

19
src/navigation/BottomTabNavigator.js

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { View, StatusBar } from 'react-native'; import { StatusBar, View, useColorScheme } from 'react-native';
import { CommonActions } from '@react-navigation/native'; import { CommonActions } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { BottomNavigation } from 'react-native-paper'; import { BottomNavigation } from 'react-native-paper';
@ -10,10 +10,17 @@ import NotifikasiScreen from '../screens/Notification';
import ProfileScreen from '../screens/Profile'; import ProfileScreen from '../screens/Profile';
import ServiceScreen from '../screens/Service'; import ServiceScreen from '../screens/Service';
import { colors } from '../utils/color'; import { colors } from '../utils/color';
import { useSelector } from 'react-redux';
const Tab = createBottomTabNavigator(); const Tab = createBottomTabNavigator();
export default function BottomTabNavigator() { export default function BottomTabNavigator() {
const { theme, useSystemTheme } = useSelector(state => state.themeReducer);
const tabBarColor = theme === 'dark' ? colors.black : colors.pureWhite;
const activeColor = colors.blue;
return ( return (
<Tab.Navigator <Tab.Navigator
screenOptions={{ screenOptions={{
@ -22,7 +29,7 @@ export default function BottomTabNavigator() {
screenListeners={({ route, navigation }) => ({ screenListeners={({ route, navigation }) => ({
focus: () => { focus: () => {
if (route.name === 'Home') { if (route.name === 'Home') {
StatusBar.setBackgroundColor(colors.blue); StatusBar.setBackgroundColor(theme === 'dark' ? colors.black : colors.blue);
StatusBar.setBarStyle('light-content'); StatusBar.setBarStyle('light-content');
} else { } else {
StatusBar.setBackgroundColor('white'); StatusBar.setBackgroundColor('white');
@ -56,14 +63,14 @@ export default function BottomTabNavigator() {
renderIcon={({ route, focused }) => { renderIcon={({ route, focused }) => {
const { options } = descriptors[route.key]; const { options } = descriptors[route.key];
if (options.tabBarIcon) { if (options.tabBarIcon) {
return options.tabBarIcon({ focused, color: colors.blue, size: 24 }); return options.tabBarIcon({ focused, color: activeColor, size: 24 });
} }
return null; return null;
}} }}
renderIndicator={({ route, focused }) => { renderIndicator={({ route, focused }) => {
if (focused) { if (focused) {
return <View style={{ height: 4, backgroundColor: colors.blue }} />; return <View style={{ height: 4, backgroundColor: colors.semiBlue }} />;
} }
return null; return null;
}} }}
@ -79,10 +86,10 @@ export default function BottomTabNavigator() {
return label; return label;
}} }}
style={{ backgroundColor: colors.pureWhite, borderTopWidth: 0, elevation: 8 }} style={{ backgroundColor: tabBarColor, borderTopWidth: 0, elevation: 8 }}
labeled={false} labeled={false}
compact={true} compact={true}
activeColor={colors.beanRed} activeColor={activeColor}
/> />
)} )}
> >

53
src/screens/Home.js

@ -7,7 +7,7 @@ import {
PermissionsAndroid, PermissionsAndroid,
RefreshControl RefreshControl
} from 'react-native'; } from 'react-native';
import { Card, Avatar, IconButton, Button, Text, TouchableRipple, ActivityIndicator } from 'react-native-paper'; import { Card, Avatar, IconButton, Button, Text, TouchableRipple, ActivityIndicator, useTheme } from 'react-native-paper';
import { launchCamera } from 'react-native-image-picker'; import { launchCamera } from 'react-native-image-picker';
import { useFocusEffect } from '@react-navigation/native'; import { useFocusEffect } from '@react-navigation/native';
import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker'; import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-marker';
@ -24,18 +24,19 @@ import {
import Icon from 'react-native-vector-icons/AntDesign'; import Icon from 'react-native-vector-icons/AntDesign';
import Toast from 'react-native-toast-message'; import Toast from 'react-native-toast-message';
import person from '../assets/images/courier_man.png';
import { colors } from '../utils/color'; import { colors } from '../utils/color';
import { getCoords } from '../utils/geolocation'; import { getCoords } from '../utils/geolocation';
import { strings } from '../utils/i18n'; import { strings } from '../utils/i18n';
import { PATH_PRESENCE } from '../config/imageFolder'; import { PATH_PRESENCE } from '../config/imageFolder';
import { storeData, resendFailedAttachments } from '../services/sqlite/attachment'; // import { storeData, resendFailedAttachments } from '../services/sqlite/attachment';
import RequestModule from '../services/api/request'; import RequestModule from '../services/api/request';
import { requestAccessStoragePermission } from '../utils/storage'; import { requestAccessStoragePermission } from '../utils/storage';
// import renderSkeleton from '../components/renderSkeleton'; import renderSkeleton from '../components/renderSkeleton';
moment.locale('id'); moment.locale('id');
const HomeScreen = ({ route, navigation }) => { const HomeScreen = ({ route, navigation }) => {
const theme = useTheme();
const isDarkTheme = theme.dark;
const request = new RequestModule('presence') const request = new RequestModule('presence')
const { user } = useSelector(state => state.userReducer) const { user } = useSelector(state => state.userReducer)
const [currentTime, setCurrentTime] = useState(moment(new Date())); const [currentTime, setCurrentTime] = useState(moment(new Date()));
@ -74,7 +75,7 @@ const HomeScreen = ({ route, navigation }) => {
} }
getPresenceHistory() getPresenceHistory()
getLastPresence() getLastPresence()
resendFailedAttachments(); // resendFailedAttachments();
}, []) }, [])
@ -173,7 +174,7 @@ const HomeScreen = ({ route, navigation }) => {
console.error("Error sending presence data:", error); console.error("Error sending presence data:", error);
Toast.show({ Toast.show({
type: 'error', type: 'error',
text1: strings('presence.errorMessage'), text1: strings('global.errorConnectionMsg'),
}); });
setLoading(false) setLoading(false)
} }
@ -377,13 +378,13 @@ const HomeScreen = ({ route, navigation }) => {
return ( return (
<View style={styles.container}> <View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }]}>
<StatusBar backgroundColor={colors.blue} barStyle='ligth-content' translucent={true} /> <StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.blue} barStyle={isDarkTheme ? 'light-content' : 'light-content'} translucent={true} />
<View style={[styles.header, { backgroundColor: colors.blue }]}> <View style={[styles.header, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.blue }]}>
<Avatar.Image style={styles.avatar} source={{ uri: 'https://nawakara-staging-api.ospro.id/assets/files/presence/2024-06/OQ8oglNiCIoCrPX1718098448814614133-0.jpg' }} /> <Avatar.Image style={styles.avatar} source={{ uri: 'https://nawakara-staging-api.ospro.id/assets/files/presence/2024-06/OQ8oglNiCIoCrPX1718098448814614133-0.jpg' }} />
<View style={styles.textContainer}> <View style={styles.textContainer}>
<Text style={[styles.welcomeText, { color: colors.pureWhite }]}> <Text style={[styles.welcomeText, { color: colors.pureWhite }]}>
{strings('home.welcomeMessage')}, {user?.name} {strings('home.welcomeMessage')}, {user?.name.length > 5 ? `${user?.name.substring(0, 5)}...` : user?.name}
</Text> </Text>
<Text style={[styles.subWelcomeText, { color: colors.pureWhite }]}> <Text style={[styles.subWelcomeText, { color: colors.pureWhite }]}>
penuh semangat dan produktivitas penuh semangat dan produktivitas
@ -397,7 +398,7 @@ const HomeScreen = ({ route, navigation }) => {
/> />
</View> </View>
<Card elevation={4} style={[styles.card, { backgroundColor: 'white' }]}> <Card elevation={4} style={[styles.card, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<Card.Content> <Card.Content>
<Text variant='displaySmall' style={[styles.boldText, { paddingBottom: 10, textAlign: 'center', color: colors.blue }]}>{moment(currentTime).format('HH:mm')}</Text> <Text variant='displaySmall' style={[styles.boldText, { paddingBottom: 10, textAlign: 'center', color: colors.blue }]}>{moment(currentTime).format('HH:mm')}</Text>
<Text variant='titleSmall' style={{ paddingBottom: 10 }}>{moment(currentTime).format('dddd, DD MMMM - YYYY')} </Text> <Text variant='titleSmall' style={{ paddingBottom: 10 }}>{moment(currentTime).format('dddd, DD MMMM - YYYY')} </Text>
@ -431,12 +432,12 @@ const HomeScreen = ({ route, navigation }) => {
</Card> </Card>
<View style={[styles.row, { justifyContent: 'space-between', paddingBottom: 5, paddingHorizontal: 15 }]}> <View style={[styles.row, { justifyContent: 'space-between', paddingBottom: 5, paddingHorizontal: 15 }]}>
<Text style={{ color: colors.black, fontWeight: 'bold' }}>Riwayat Kehadiran</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black, fontWeight: 'bold' }}>Riwayat Kehadiran</Text>
<TouchableRipple onPress={() => { navigation.navigate('PresenceScreen') }}> <TouchableRipple onPress={() => { navigation.navigate('PresenceScreen') }}>
<Text style={{ color: colors.amethystSmoke }}>Lihat Semua</Text> <Text style={{ color: colors.amethystSmoke }}>Lihat Semua</Text>
</TouchableRipple> </TouchableRipple>
</View> </View>
<ScrollView style={{ flex: 1 }} <ScrollView style={{ flex: 1, backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite }}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={refreshing} refreshing={refreshing}
@ -447,15 +448,15 @@ const HomeScreen = ({ route, navigation }) => {
} }
> >
{loadingSkeleton ? ( {loadingSkeleton ? (
// renderSkeleton(2) renderSkeleton(2, theme)
null
) : ( ) : (
dataPresence.map((item) => ( dataPresence.map((item) => (
<Card key={item.id} style={styles.cardPrecense}> <Card key={item.id} style={[styles.cardPrecense, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<Card.Content> <Card.Content>
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={[styles.row, styles.precenseDate]}> <View style={[styles.row, styles.precenseDate]}>
<Icon name="calendar" size={20} color={colors.blue} /> <Icon name="calendar" size={20} color={colors.blue} />
<Text style={{ color: colors.blue, fontWeight: 'bold', paddingLeft: 3 }}> <Text style={{ color: colors.blue, fontWeight: 'bold', paddingLeft: 3 }}>
{moment(item.datePresence).format('dddd, DD MMMM - YYYY')} {moment(item.datePresence).format('dddd, DD MMMM - YYYY')}
</Text> </Text>
@ -469,28 +470,29 @@ const HomeScreen = ({ route, navigation }) => {
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={styles.row}> <View style={styles.row}>
<Icon name="clockcircleo" size={20} color={colors.black} /> <Icon name="clockcircleo" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />
<Text style={{ color: colors.amethystSmoke, fontWeight: 'bold', paddingLeft: 3 }}> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke, fontWeight: 'bold', paddingLeft: 3, marginLeft: 3 }}>
Durasi Kerja Durasi Kerja
</Text> </Text>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Text style={{ color: colors.amethystSmoke }}>Masuk</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>Masuk</Text>
<Icon name="minus" size={20} color={colors.amethystSmoke} /> <Icon name="minus" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke} />
<Text style={{ color: colors.amethystSmoke }}>Keluar</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>Keluar</Text>
</View> </View>
</View> </View>
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={styles.row}> <View style={styles.row}>
<Text style={{ color: colors.black, paddingLeft: 5 }}>{item.duration ? convertDuration(item.duration) : ''}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke, paddingLeft: 5 }}>{item.duration ? convertDuration(item.duration) : ''}</Text>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Text>{moment(item.in_time).format('HH:mm')}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>{moment(item.in_time).format('HH:mm')}</Text>
<Icon name="minus" size={20} color={colors.black} /> <Icon name="minus" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke} />
<Text>{item.out_time ? moment(item.out_time).format('HH:mm') : ''}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>{item.out_time ? moment(item.out_time).format('HH:mm') : ''}</Text>
</View> </View>
</View> </View>
</Card.Content> </Card.Content>
</Card> </Card>
)) ))
)} )}
@ -593,7 +595,6 @@ const styles = StyleSheet.create({
marginHorizontal: 10, marginHorizontal: 10,
marginVertical: 2, marginVertical: 2,
elevation: 4, elevation: 4,
backgroundColor: colors.pureWhite
}, },
loading: { loading: {
position: 'absolute', position: 'absolute',

87
src/screens/Login.js

@ -1,21 +1,19 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { KeyboardAvoidingView, ScrollView, StatusBar, View, StyleSheet, Image, Platform, Dimensions, Alert } from 'react-native'; import { KeyboardAvoidingView, ScrollView, StatusBar, View, StyleSheet, Image, Platform, Dimensions } from 'react-native';
import { Button, TextInput, HelperText, Text, Modal } from 'react-native-paper'; import { Button, TextInput, HelperText, Text, useTheme } from 'react-native-paper';
import { useDispatch } from 'react-redux';
import { setIsLogin, setUser, setRegister } from '../appredux/actions'; import { setIsLogin, setUser, setRegister } from '../appredux/actions';
import { store } from '../appredux/store';
import { colors } from '../utils/color';
import LOGIN_BANNER from '../assets/images/logo_nawakara.png'; import LOGIN_BANNER from '../assets/images/logo_nawakara.png';
import { strings } from '../utils/i18n'; import { strings } from '../utils/i18n';
import { initFirebase } from '../utils/Auth'; import { initFirebase } from '../utils/Auth';
import RequestModule from '../services/api/request'; import RequestModule from '../services/api/request';
import Toast from 'react-native-toast-message'; import Toast from 'react-native-toast-message';
const LoginScreen = ({ route, navigation }) => { const LoginScreen = ({ navigation }) => {
const theme = useTheme();
const dispatch = useDispatch();
const request = new RequestModule(''); const request = new RequestModule('');
const [visible, setVisible] = useState(false);
const showModal = () => setVisible(true);
const hideModal = () => setVisible(false);
const [loginProcess, setLoginProcess] = useState(false); const [loginProcess, setLoginProcess] = useState(false);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const { control, handleSubmit, formState: { errors, isDirty, isValid, isSubmitting } } = useForm({ const { control, handleSubmit, formState: { errors, isDirty, isValid, isSubmitting } } = useForm({
@ -30,19 +28,18 @@ const LoginScreen = ({ route, navigation }) => {
initFirebase(); initFirebase();
}, []); }, []);
const onSubmitLogin = async (payload) => { const onSubmitLogin = async (payload) => {
setLoginProcess(true); setLoginProcess(true);
try { try {
const result = await request.login(payload); const result = await request.login(payload);
if (result && result.status === 200 && result.data.data) { if (result && result.status === 200 && result.data.data) {
store.dispatch(setIsLogin(true)); dispatch(setIsLogin(true));
store.dispatch(setUser(result.data.data)); dispatch(setUser(result.data.data));
if (result.data.data.assigment_hr) { if (result.data.data.assigment_hr) {
store.dispatch(setRegister(true)); dispatch(setRegister(true));
navigation.navigate('App'); navigation.navigate('App');
} else { } else {
store.dispatch(setRegister(false)); dispatch(setRegister(false));
navigation.navigate('RegisterScreen'); navigation.navigate('RegisterScreen');
} }
} else { } else {
@ -65,29 +62,27 @@ const LoginScreen = ({ route, navigation }) => {
return ( return (
<> <>
<StatusBar backgroundColor={colors.white} barStyle='dark-content' translucent={true} /> <StatusBar backgroundColor={theme.colors.background} barStyle={theme.dark ? 'light-content' : 'dark-content'} translucent={true} />
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : null} style={{ flex: 1 }}> <KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : null} style={{ flex: 1 }}>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}> <ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={styles.container}> <View style={[styles.container, { backgroundColor: theme.colors.background }]}>
<Image <Image
alt="login-image" alt="login-image"
source={LOGIN_BANNER} source={LOGIN_BANNER}
style={{ width: '100%', height: Dimensions.get('window').height / 5, marginTop: 30, marginBottom: 20 }} style={styles.loginBanner}
resizeMode="contain" resizeMode="contain"
/> />
<Text variant="displaySmall" style={{ color: colors.black, fontFamily: 'Roboto-Bold' }}>{strings('loginPage.headMessage')}</Text> <Text variant="displaySmall" style={[styles.headMessage, { color: theme.colors.text }]}>{strings('loginPage.headMessage')}</Text>
<Text variant="bodyMedium" style={{ color: colors.mistBlue }}>Silahkan isi Username dan kata sandi anda</Text> <Text variant="bodyMedium" style={{ color: theme.colors.text }}>Silahkan isi Username dan kata sandi anda</Text>
<Controller <Controller
control={control} control={control}
rules={{ rules={{ required: true }}
required: true,
minLength: 0
}}
render={({ field: { onChange, onBlur, value } }) => ( render={({ field: { onChange, onBlur, value } }) => (
<TextInput <TextInput
underlineColor={colors.amethystSmoke} underlineColor={theme.colors.placeholder}
activeOutlineColor={colors.blue} activeOutlineColor={theme.colors.blue}
mode="outlined" mode="outlined"
label="Username" label="Username"
onBlur={onBlur} onBlur={onBlur}
@ -100,20 +95,19 @@ const LoginScreen = ({ route, navigation }) => {
)} )}
name="username" name="username"
/> />
{errors.username && {errors.username && (
<HelperText type="error" padding='none' visible={!!errors.username}> <HelperText type="error" padding='none' visible={!!errors.username}>
{strings('loginPage.usernameErrorMsg')} {strings('loginPage.usernameErrorMsg')}
</HelperText>} </HelperText>
)}
<Controller <Controller
control={control} control={control}
rules={{ rules={{ required: true, minLength: 3 }}
required: true,
minLength: 3
}}
render={({ field: { onChange, onBlur, value } }) => ( render={({ field: { onChange, onBlur, value } }) => (
<TextInput <TextInput
underlineColor={colors.amethystSmoke} underlineColor={theme.colors.placeholder}
activeOutlineColor={colors.blue} activeOutlineColor={theme.colors.blue}
mode="outlined" mode="outlined"
label="Password" label="Password"
onBlur={onBlur} onBlur={onBlur}
@ -132,27 +126,26 @@ const LoginScreen = ({ route, navigation }) => {
)} )}
name="password" name="password"
/> />
{errors.password && {errors.password && (
<HelperText type="error" padding='none' visible={!!errors.password}> <HelperText type="error" padding='none' visible={!!errors.password}>
{strings('loginPage.passwordErrorMsg')} {strings('loginPage.passwordErrorMsg')}
</HelperText>} </HelperText>
)}
<Button <Button
mode="contained" mode="contained"
onPress={handleSubmit(onSubmitLogin)} onPress={handleSubmit(onSubmitLogin)}
style={{ marginTop: 15, backgroundColor: colors.blue, borderRadius: 10 }} style={[styles.signInButton, { backgroundColor: theme.colors.blue }]}
disabled={!isDirty || !isValid || isSubmitting || loginProcess} disabled={!isDirty || !isValid || isSubmitting || loginProcess}
labelStyle={{ color: colors.white }} labelStyle={{ color: theme.colors.background }}
loading={isSubmitting || loginProcess} loading={isSubmitting || loginProcess}
> >
{strings('loginPage.signInBtn')} {strings('loginPage.signInBtn')}
</Button> </Button>
</View> </View>
</ScrollView > </ScrollView>
</KeyboardAvoidingView> </KeyboardAvoidingView>
</> </>
) )
} }
@ -161,12 +154,24 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
padding: 20, padding: 20,
justifyContent: 'center', justifyContent: 'center',
backgroundColor: colors.white,
paddingBottom: 100 paddingBottom: 100
}, },
eyeIcon: { eyeIcon: {
marginTop: 12 marginTop: 12
},
loginBanner: {
width: '100%',
height: Dimensions.get('window').height / 5,
marginTop: 30,
marginBottom: 20
},
headMessage: {
fontFamily: 'Roboto-Bold'
},
signInButton: {
marginTop: 15,
borderRadius: 10
} }
}) });
export default LoginScreen; export default LoginScreen;

154
src/screens/Profile.js

@ -1,110 +1,115 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Alert, View, ScrollView, StyleSheet, TouchableOpacity, StatusBar } from 'react-native'; import { Alert, View, ScrollView, StyleSheet, StatusBar } from 'react-native';
import { Avatar, Text, Appbar, List, Button, Switch, Modal, Dialog, TouchableRipple } from 'react-native-paper'; import { Avatar, Text, Appbar, List, Button, Dialog, TouchableRipple, PaperProvider, useTheme } from 'react-native-paper';
import AntDesign from 'react-native-vector-icons/AntDesign';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { clearAllState } from '../utils/Auth';
import { strings } from '../utils/i18n'; import { strings } from '../utils/i18n';
import person from '../assets/images/courier_man.png'
import { colors } from '../utils/color'; import { colors } from '../utils/color';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { clearAllState } from '../utils/Auth';
const ProfileScreen = ({ navigation }) => { const ProfileScreen = ({ navigation }) => {
const [isSwitchOn, setIsSwitchOn] = React.useState(false); const theme = useTheme();
const { user } = useSelector(state => state.userReducer) const isDarkTheme = theme.dark;
const onToggleSwitch = () => setIsSwitchOn(!isSwitchOn); const { user } = useSelector(state => state.userReducer);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const showDialog = () => setVisible(true);
const showModal = () => setVisible(true); const hideDialog = () => setVisible(false);
const hideModal = () => setVisible(false);
console.log("user", user.join);
return ( return (
<> <PaperProvider>
<View style={styles.container}> <View style={styles.container}>
<StatusBar backgroundColor={colors.pureWhite} barStyle='dark-content' translucent={true} /> <StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.blue} barStyle={isDarkTheme ? 'dark-content' : 'dark-content'} translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.pureWhite }} > <Appbar.Header mode='center-aligned' style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }}>
<Appbar.BackAction onPress={() => { navigation.goBack() }} /> <Appbar.BackAction color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} onPress={() => { navigation.goBack() }} />
<Appbar.Content title="Profile" /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} title={strings('profile.profile')} />
</Appbar.Header> </Appbar.Header>
<View style={styles.header}> <View style={[styles.header, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<Avatar.Image style={styles.avatar} size={80} source={{ uri: 'https://nawakara-staging-api.ospro.id/assets/files/presence/2024-06/OQ8oglNiCIoCrPX1718098448814614133-0.jpg' }} /> <Avatar.Image style={styles.avatar} size={80} source={{ uri: 'https://nawakara-staging-api.ospro.id/assets/files/presence/2024-06/OQ8oglNiCIoCrPX1718098448814614133-0.jpg' }} />
<View style={styles.textContainer}> <View style={styles.textContainer}>
<Text style={[styles.welcomeText, { color: colors.black }]}> <Text style={[styles.welcomeText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }]}>
{user.name} {user.name}
</Text> </Text>
<Text style={[styles.subWelcomeText, { color: colors.amethystSmoke }]}> <Text style={[styles.subWelcomeText, { color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }]}>
{user.join.m_role_name} {user.assigment_hr.position}
</Text> </Text>
</View> </View>
</View> </View>
<ScrollView style={{ backgroundColor: colors.pureWhite }}> <ScrollView style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }}>
<View style={styles.cardView}> <View style={styles.cardView}>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('DailyReportScreen') }}> <TouchableRipple rippleColor={isDarkTheme ? theme.colors.amethystSmoke : theme.colors.pureWhite} onPress={() => { navigation.navigate('DailyReportScreen') }}>
<List.Item <List.Item
title="Laporan Harian" title={strings('dailyReport.title')}
left={() => <List.Icon icon="file-document" />} titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
right={() => <List.Icon icon="chevron-right" />} left={() => <List.Icon icon="file-document" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('PresenceScreen') }}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('PresenceScreen') }}>
<List.Item <List.Item
title="Riwayat Kehadiran" title={strings('presence.attendanceHistory')}
left={() => <List.Icon icon="history" />} titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
right={() => <List.Icon icon="chevron-right" />} left={() => <List.Icon icon="history" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('RegisterScreen') }}> <TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('RegisterScreen') }}>
<List.Item <List.Item
title="Registrasi" title={strings('register.title')}
left={() => <List.Icon icon="account-cog" />} titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
right={() => <List.Icon icon="chevron-right" />} left={() => <List.Icon icon="account-cog" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('RegisterScreen') }}> {user.assigment_hr.position !== 'Guard' &&
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('RegisterScreen') }}>
<List.Item
title={strings('profile.personnelList')}
titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
left={() => <List.Icon icon="account" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/>
</TouchableRipple>
}
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('ThemeScreen') }}>
<List.Item <List.Item
title="Daftar Personel" title={strings('profile.changeTheme')}
left={() => <List.Icon icon="account" />} titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
right={() => <List.Icon icon="chevron-right" />} left={() => <List.Icon icon="theme-light-dark" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/>
</TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={() => { navigation.navigate('LanguageScreen') }}>
<List.Item
title={strings('profile.languageSetting')}
titleStyle={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}
left={() => <List.Icon icon="translate" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
right={() => <List.Icon icon="chevron-right" color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />}
/> />
</TouchableRipple> </TouchableRipple>
<List.Item
title="bahasa"
left={() => <List.Icon icon="translate" />}
right={() =>
<View style={styles.switchContainer}>
<Text>{isSwitchOn ? "ID" : "EN"}</Text>
<Switch
trackColor={{ true: colors.blue, false: colors.blue }}
color={colors.pureWhite}
style={styles.switch}
onValueChange={onToggleSwitch}
value={isSwitchOn}
/>
</View>
}
/>
</View> </View>
</ScrollView> </ScrollView>
<Button buttonColor={colors.beanRed} style={styles.button} contentStyle={{ flexDirection: 'row-reverse' }} icon="logout" mode="contained" onPress={showModal}> <View style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }}>
Keluar <Button buttonColor={colors.beanRed} textColor={theme.colors.pureWhite} style={styles.button} contentStyle={{ flexDirection: 'row-reverse' }} icon="logout" mode="contained" onPress={showDialog}>
</Button> {strings('global.logout')}
</Button>
</View>
</View > </View >
<Dialog visible={visible} onDismiss={hideDialog} style={[styles.modal, { 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="bodyMedium" style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.black }}>{strings('profile.signoutMessage')}</Text>
<Button buttonColor={colors.blue} textColor={theme.colors.pureWhite} style={{ borderRadius: 10, marginVertical: 20, paddingVertical: 5 }} contentStyle={{ flexDirection: 'row-reverse' }} mode="contained" onPress={() => { clearAllState() }}>
{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>
<Modal visible={visible} onDismiss={hideModal} contentContainerStyle={styles.modal}> </PaperProvider >
<Text variant='titleLarge' >Konfirmasi</Text>
<Text variant='bodyMedium' >{strings('profile.signoutMessage')}</Text>
<Button buttonColor={colors.blue} style={{ borderRadius: 10, marginVertical: 20, paddingVertical: 5 }} contentStyle={{ flexDirection: 'row-reverse' }} mode="contained" onPress={() => { clearAllState() }}>
{strings('global.yes')}
</Button>
<Button style={{ borderRadius: 10, paddingVertical: 5 }} contentStyle={{ flexDirection: 'row-reverse' }} textColor={colors.mistBlue} mode="outlined" onPress={hideModal}>
{strings('global.no')}
</Button>
</Modal>
</>
) )
} }
@ -112,14 +117,12 @@ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 20, marginTop: 20,
backgroundColor: colors.pureWhite
}, },
header: { header: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 10, paddingHorizontal: 10,
paddingVertical: 20, paddingVertical: 20,
backgroundColor: colors.pureWhite
}, },
avatar: { avatar: {
marginRight: 10, marginRight: 10,
@ -144,7 +147,6 @@ const styles = StyleSheet.create({
marginTop: 10, marginTop: 10,
marginHorizontal: 10, marginHorizontal: 10,
marginVertical: 2, marginVertical: 2,
backgroundColor: colors.pureWhite
}, },
button: { button: {
margin: 10, margin: 10,
@ -156,17 +158,9 @@ const styles = StyleSheet.create({
padding: 20, padding: 20,
marginHorizontal: 20, marginHorizontal: 20,
borderRadius: 10, borderRadius: 10,
backgroundColor: colors.pureWhite
},
switchContainer: {
flexDirection: "row",
justifyContent: "center", justifyContent: "center",
alignItems: "center" alignItems: "center"
},
switch: {
marginLeft: 10
} }
}) })
export default ProfileScreen export default ProfileScreen

13
src/screens/Service.js

@ -1,6 +1,6 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { Icon, Card, Text, Avatar, useTheme, IconButton, MD3Colors, Button, TouchableRipple } from 'react-native-paper'; import { Icon, Card, Text, Avatar, useTheme, IconButton, MD3Colors, Button, TouchableRipple } from 'react-native-paper';
import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions } from 'react-native'; import { RefreshControl, StyleSheet, View, ScrollView, Image, Dimensions, StatusBar } from 'react-native';
import ilustration from '../assets/images/checked.png'; import ilustration from '../assets/images/checked.png';
import danger from '../assets/images/danger.png'; import danger from '../assets/images/danger.png';
import activity from '../assets/images/activity.png'; import activity from '../assets/images/activity.png';
@ -12,8 +12,10 @@ import { strings } from '../utils/i18n';
export default function ServiceScreen({ route, navigation }) { export default function ServiceScreen({ route, navigation }) {
const theme = useTheme(); const theme = useTheme();
const isDarkTheme = theme.dark
return ( return (
<View style={styles.container}> <View style={[styles.container, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.blue} barStyle={isDarkTheme ? 'dark-content' : 'dark-content'} translucent={true} />
<Card elevation={4} style={{ marginHorizontal: 10, marginVertical: 6, marginTop: 10, backgroundColor: colors.beanRed }}> <Card elevation={4} style={{ marginHorizontal: 10, marginVertical: 6, marginTop: 10, backgroundColor: colors.beanRed }}>
<TouchableRipple onPress={() => { navigation.navigate('IncidentScreen') }}> <TouchableRipple onPress={() => { navigation.navigate('IncidentScreen') }}>
<View style={styles.cardContent}> <View style={styles.cardContent}>
@ -102,8 +104,8 @@ export default function ServiceScreen({ route, navigation }) {
/> />
<Text style={[styles.Text, { color: colors.pureWhite }]}>Kehadiran</Text> <Text style={[styles.Text, { color: colors.pureWhite }]}>Kehadiran</Text>
<Text variant="bodySmall" style={[styles.subText, { color: colors.pureWhite }]} >Lihat riwayat kehadiran</Text> <Text variant="bodySmall" style={[styles.subText, { color: colors.pureWhite }]} >Lihat riwayat kehadiran</Text>
<Button mode="contained" compact={true} style={{ borderRadius: 10, marginTop: 10, marginBottom: 5, backgroundColor: 'white', width: '100%' }} labelStyle={{ color: colors.blue }} onPress={() => { navigation.navigate('PresenceScreen') }}> <Button mode="contained" compact={true} style={{ borderRadius: 10, marginTop: 10, backgroundColor: 'white', width: '100%' }} labelStyle={{ color: colors.blue }} onPress={() => { navigation.navigate('PresenceScreen') }}>
{strings('global.see')} {strings('global.fill')}
</Button> </Button>
</View> </View>
</TouchableRipple> </TouchableRipple>
@ -134,8 +136,7 @@ export default function ServiceScreen({ route, navigation }) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
marginTop: 30, marginTop: 30
backgroundColor: colors.pureWhite
}, },
Text: { Text: {
fontSize: 16, fontSize: 16,

80
src/screens/presence/index.js

@ -1,4 +1,4 @@
import { TouchableRipple, TextInput, Card, Text, Appbar } from 'react-native-paper'; import { TouchableRipple, TextInput, Card, Text, Appbar, Avatar, ActivityIndicator, useTheme } from 'react-native-paper';
import { View, StyleSheet, ScrollView, RefreshControl, StatusBar, Image } from 'react-native'; import { View, StyleSheet, ScrollView, RefreshControl, StatusBar, Image } from 'react-native';
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react'; import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { colors } from '../../utils/color' import { colors } from '../../utils/color'
@ -12,10 +12,13 @@ import {
BottomSheetModalProvider, BottomSheetModalProvider,
BottomSheetView BottomSheetView
} from '@gorhom/bottom-sheet'; } from '@gorhom/bottom-sheet';
import { strings } from '../../utils/i18n';
moment.locale('id'); moment.locale('id');
const PAGE_SIZE = 25; const PAGE_SIZE = 25;
export default function PresenceScreen({ route, navigation }) { export default function PresenceScreen({ route, navigation }) {
const theme = useTheme();
const isDarkTheme = theme.dark
const request = new RequestModule('presence'); const request = new RequestModule('presence');
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
@ -23,10 +26,16 @@ export default function PresenceScreen({ route, navigation }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const bottomSheetImage = useRef(null); const bottomSheetImage = useRef(null);
const [selectedImageUri, setSelectedImageUri] = useState(null); const [selectedImageUri, setSelectedImageUri] = useState(null);
const [loadingImage, setLoadingImage] = useState(false);
const handleOpenSheetImage = useCallback((uri) => { const handleOpenSheetImage = useCallback((uri) => {
console.log('URI received:', uri); setLoadingImage(true);
setSelectedImageUri(uri); setTimeout(() => {
bottomSheetImage.current?.present(); setSelectedImageUri(uri);
setLoadingImage(false);
bottomSheetImage.current?.present();
}, 1000);
}, []); }, []);
const onRefresh = useCallback(() => { const onRefresh = useCallback(() => {
@ -73,27 +82,34 @@ export default function PresenceScreen({ route, navigation }) {
const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => { const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20; return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
}; };
const renderImage = useMemo(() => { const renderImage = useMemo(() => {
console.log('Rendering image with URI:', selectedImageUri);
return ( return (
<Image <>
source={{ uri: selectedImageUri }} {loadingImage ? (
style={styles.imageDetail} <ActivityIndicator size="large" color={colors.blue} />
resizeMode="cover" ) : (
/> <>
<Image
source={{ uri: selectedImageUri }}
style={styles.imageDetail}
resizeMode="cover"
/>
</>
)}
</>
); );
}, [selectedImageUri]); }, [selectedImageUri, loadingImage]);
return ( return (
<> <>
<StatusBar backgroundColor={colors.blue} barStyle='ligth-content' translucent={true} /> <StatusBar backgroundColor={isDarkTheme ? theme.colors.background : theme.colors.blue} barStyle={isDarkTheme ? 'dark-content' : 'light-content'} translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.blue }}> <Appbar.Header mode='center-aligned' style={{ backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.blue }}>
<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='Riwayat kehadiran' /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('presence.attendanceHistory')} />
</Appbar.Header> </Appbar.Header>
<ScrollView <ScrollView
style={{ flex: 1, marginTop: 5 }} style={{ flex: 1, marginTop: 5, backgroundColor: theme.colors.surface }}
refreshControl={ refreshControl={
<RefreshControl <RefreshControl
refreshing={refreshing} refreshing={refreshing}
@ -106,12 +122,11 @@ export default function PresenceScreen({ route, navigation }) {
scrollEventThrottle={400} scrollEventThrottle={400}
> >
{loading ? ( {loading ? (
// renderSkeleton(10) renderSkeleton(10, theme)
null
) : ( ) : (
dataPresence.map((item) => ( dataPresence.map((item) => (
<TouchableRipple onPress={() => item.attachments && item.attachments[0] && handleOpenSheetImage(item.attachments[0].url)} key={item.id}> <TouchableRipple onPress={() => item.attachments && item.attachments[0] && handleOpenSheetImage(item.attachments[0].url)} key={item.id}>
<Card key={item.id} style={styles.cardPrecense}> <Card key={item.id} style={[styles.cardPrecense, { backgroundColor: isDarkTheme ? theme.colors.background : theme.colors.pureWhite }]}>
<Card.Content> <Card.Content>
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={[styles.row, styles.precenseDate]}> <View style={[styles.row, styles.precenseDate]}>
@ -130,35 +145,34 @@ export default function PresenceScreen({ route, navigation }) {
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={styles.row}> <View style={styles.row}>
<Icon name="clockcircleo" size={20} color={colors.black} /> <Icon name="clockcircleo" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.black} />
<Text style={{ color: colors.amethystSmoke, fontWeight: 'bold', paddingLeft: 3 }}> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke, fontWeight: 'bold', paddingLeft: 3, marginLeft: 3 }}>
Durasi Kerja Durasi Kerja
</Text> </Text>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Text style={{ color: colors.amethystSmoke }}>Masuk</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>Masuk</Text>
<Icon name="minus" size={20} color={colors.amethystSmoke} /> <Icon name="minus" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke} />
<Text style={{ color: colors.amethystSmoke }}>Keluar</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>Keluar</Text>
</View> </View>
</View> </View>
<View style={[styles.row, { justifyContent: 'space-between' }]}> <View style={[styles.row, { justifyContent: 'space-between' }]}>
<View style={styles.row}> <View style={styles.row}>
<Text style={{ color: colors.black, paddingLeft: 5 }}>{item.duration ? convertDuration(item.duration) : ''}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke, paddingLeft: 5 }}>{item.duration ? convertDuration(item.duration) : ''}</Text>
</View> </View>
<View style={styles.row}> <View style={styles.row}>
<Text>{moment(item.in_time).format('HH:mm')}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>{moment(item.in_time).format('HH:mm')}</Text>
<Icon name="minus" size={20} color={colors.black} /> <Icon name="minus" size={20} color={isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke} />
<Text>{item.out_time ? moment(item.out_time).format('HH:mm') : ''}</Text> <Text style={{ color: isDarkTheme ? theme.colors.pureWhite : theme.colors.amethystSmoke }}>{item.out_time ? moment(item.out_time).format('HH:mm') : ''}</Text>
</View> </View>
</View> </View>
<Text>{item.attachments && item.attachments.length > 0 ? item.attachments[0].url : 'No attachments'}</Text>
</Card.Content> </Card.Content>
</Card> </Card>
</TouchableRipple> </TouchableRipple>
)) ))
)} )}
</ScrollView> </ScrollView >
<BottomSheetModalProvider> <BottomSheetModalProvider>
<BottomSheetModal <BottomSheetModal
@ -239,6 +253,10 @@ const styles = StyleSheet.create({
marginHorizontal: 10, marginHorizontal: 10,
marginVertical: 2, marginVertical: 2,
elevation: 4, elevation: 4,
backgroundColor: colors.pureWhite
}, },
imageDetail: {
width: 330,
height: 300,
borderRadius: 7
}
}); });

248
src/screens/registerPage/index.js

@ -1,9 +1,11 @@
import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react'; import React, { useEffect, useCallback, useMemo, useState, useRef } from 'react';
import { StatusBar } from 'react-native'; import { StatusBar } from 'react-native';
import { Button, IconButton, Text, Appbar, TextInput, TouchableRipple, Card, List } from 'react-native-paper'; import { Button, IconButton, Text, Appbar, TextInput, TouchableRipple, Card, List, 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 { colors } from '../../utils/color';
import { strings } from '../../utils/i18n'; import { strings } from '../../utils/i18n';
import { store } from '../../appredux/store';
import { setRegister } from '../../appredux/actions';
import { launchCamera } from 'react-native-image-picker'; import { launchCamera } from 'react-native-image-picker';
import { requestAccessStoragePermission } from '../../utils/storage'; import { requestAccessStoragePermission } from '../../utils/storage';
import { getCoords } from '../../utils/geolocation'; import { getCoords } from '../../utils/geolocation';
@ -14,16 +16,19 @@ import ImageMarker, { Position, TextBackgroundType } from 'react-native-image-ma
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import moment from 'moment'; import moment from 'moment';
import uuid from 'react-native-uuid'; import uuid from 'react-native-uuid';
import Toast from 'react-native-toast-message';
import RequestModule import RequestModule
from '../../services/api/request'; from '../../services/api/request';
import { PATH_ID } from '../../config/imageFolder'; import { PATH_ID } from '../../config/imageFolder';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
export default function DialogForm() { export default function DialogForm() {
const theme = useTheme();
const isDarkTheme = theme.dark;
const request = new RequestModule('project-charter') const request = new RequestModule('project-charter')
const requestAssign = new RequestModule('assign-hr-to-project') const requestAssign = new RequestModule('assignHR')
const navigation = useNavigation(); const navigation = useNavigation();
const { user } = useSelector(state => state.userReducer) const { user } = useSelector(state => state.userReducer)
const [images, setImages] = useState([]); const [image, setImage] = useState(null);
const [selectedImageUri, setSelectedImageUri] = useState(null); const [selectedImageUri, setSelectedImageUri] = useState(null);
const bottomSheetImage = useRef(null); const bottomSheetImage = useRef(null);
const [selectedProject, setSelectedProject] = useState(null); const [selectedProject, setSelectedProject] = useState(null);
@ -59,10 +64,11 @@ export default function DialogForm() {
}, []); }, []);
useEffect(() => { useEffect(() => {
handleGetProjectCharter()
handleGetAssignHR()
if (existingAttachmentNumber === '') { if (existingAttachmentNumber === '') {
setExistingAttachmentNumber(uuid.v4()) setExistingAttachmentNumber(uuid.v4())
} }
handleGetProjectCharter()
}, []) }, [])
@ -70,41 +76,98 @@ export default function DialogForm() {
navigation.navigate('SearchPage', { dataListProjectCharters, onSelect: handleProjectSelect }); navigation.navigate('SearchPage', { dataListProjectCharters, onSelect: handleProjectSelect });
}; };
const handleGetAssignHR = async () => {
const payload = {
paging: { start: 0, length: 1 },
columns: [
{ name: 'user_id', logic_operator: '=', operator: 'AND', value: user?.id.toString() },
{ name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND' },
],
orders: { columns: ['created_at', 'id'], ascending: false }
};
const result = await requestAssign.getDataSearch(payload);
if (result && result.status === 200) {
let projectName
if (result.data.data[0]?.project_charter_id) {
projectName = await getProjectName(result.data.data[0]?.project_charter_id);
}
setImage({ imageFile: result.data.data[0]?.attachments[0]?.url })
setSelectedProject({ id: result.data.data[0]?.project_charter_id, project_name: projectName || '' });
setShift(result.data?.data[0]?.shift)
setPosition(result.data?.data[0]?.position)
setExpDate(result.data?.data[0]?.created_at)
setArea(result.data?.data[0]?.area)
setSelectedPost(result.data?.data[0]?.pos)
setExistingAttachmentNumber(result.data?.data[0]?.attachment_number)
}
}
const handleSendRegisterData = async () => { const handleSendRegisterData = async () => {
const payload = { const payload = {
project_charter_id: selectedProject.id, project_charter_id: selectedProject.id,
hr_id: user.id, hr_id: user.id,
Position: position, position: position,
Area: area, area: area,
Post: selectedPost, post: selectedPost,
shift: shift,
attachment_number: existingAttachmentNumber attachment_number: existingAttachmentNumber
}; };
try { try {
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) {
store.dispatch(setRegister(true));
Toast.show({ Toast.show({
type: 'success', type: 'success',
text1: strings('presence.dataSentSuccessfully'), text1: strings('register.dataSentSuccessfully'),
}); });
navigation.navigate('App'); navigation.navigate('App');
clearForm()
} else { } else {
Toast.show({ Toast.show({
type: 'error', type: 'error',
text1: strings('presence.failedSendDataPresence'), 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({
type: 'error', type: 'error',
text1: strings('presence.errorMessage'), text1: strings('global.errorConnectionMsg'),
}); });
} finally { } finally {
setLoading(false); // Ensure setLoading is called once in the end // setLoading(false); // Ensure setLoading is called once in the end
} }
}; };
const getProjectName = async (projectCharterId) => {
const payload = {
paging: { start: 0, length: 1 },
columns: [
{ name: 'id', logic_operator: '=', operator: 'AND', value: projectCharterId.toString() },
{ name: 'deleted_at', logic_operator: 'IS NULL', operator: 'AND' },
],
orders: { columns: ['created_at', 'id'], ascending: false }
};
const result = await request.getDataSearch(payload);
if (result && result.status === 200 && result.data.data.length > 0) {
return result.data.data[0].project_name;
}
return '';
};
const clearForm = () => {
setImage(null)
setSelectedProject(null)
setShift('')
setPosition('')
setExpDate('')
setArea('')
setSelectedPost('')
setExistingAttachmentNumber('')
}
const handleProjectSelect = (project) => { const handleProjectSelect = (project) => {
handleGetDataManPowers(project.id) handleGetDataManPowers(project.id)
@ -185,18 +248,11 @@ export default function DialogForm() {
} }
}; };
const handleDeleteImage = () => {
const handleSetImageUri = (uri, description) => { setImage(null);
const newImage = { uri: uri, description: description || "" };
setImages(prevImages => [...prevImages, newImage]);
}
const handleDeleteImage = (index) => {
const updatedImages = [...images];
updatedImages.splice(index, 1);
setImages(updatedImages);
}; };
const handleTakePicture = async () => { const handleTakePicture = async () => {
try { try {
const granted = await PermissionsAndroid.request( const granted = await PermissionsAndroid.request(
@ -253,7 +309,7 @@ export default function DialogForm() {
console.log('ImagePicker Error: ', response.error); console.log('ImagePicker Error: ', response.error);
} else { } else {
if (response.assets && response.assets.length > 0) { if (response.assets && response.assets.length > 0) {
setLoading(true) // setLoading(true)
try { try {
getCoords(async (loc) => { getCoords(async (loc) => {
if (loc) { if (loc) {
@ -290,7 +346,7 @@ 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,
@ -300,27 +356,25 @@ export default function DialogForm() {
type: response.assets[0].type, type: response.assets[0].type,
name: response.assets[0].fileName name: response.assets[0].fileName
}; };
setImage(newImageData);
const resultImage = await handleUploadImage([newImageData], PATH_ID);
handleSendRegisterData();
} }
}); });
} catch (error) { } catch (error) {
console.error('Error adding overlay:', error); console.error('Error adding overlay:', error);
setLoading(false) // setLoading(false)
} }
} }
} }
}); });
}; };
const renderImages = useMemo(() => images.map((image, index) => ( const renderImage = useMemo(() => (
<> <>
<View key={index} style={styles.imageBlock}> <View style={styles.imageBlock}>
<TouchableRipple onPress={() => { handleOpenSheetImage(image.uri) }}> <TouchableRipple onPress={() => handleOpenSheetImage(image?.imageFile)}>
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
<Image <Image
source={{ uri: image.uri }} source={{ uri: image?.imageFile }}
style={styles.image} style={styles.image}
resizeMode="cover" resizeMode="cover"
/> />
@ -328,11 +382,14 @@ 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(index)}> <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>
</> </>
)), [images]); ), [image, handleOpenSheetImage, handleDeleteImage]);
const renderArea = useMemo(() => listArea?.manpower_planning?.info?.data.map((data, index) => ( const renderArea = useMemo(() => listArea?.manpower_planning?.info?.data.map((data, index) => (
<> <>
@ -356,15 +413,15 @@ export default function DialogForm() {
return ( return (
<> <>
<StatusBar backgroundColor={colors.blue} barStyle='light-content' translucent={true} /> <StatusBar backgroundColor={isDarkTheme ? theme.colors.surface : theme.colors.blue} barStyle={isDarkTheme ? 'dark-content' : 'light-content'} translucent={true} />
<Appbar.Header mode='center-aligned' style={{ backgroundColor: colors.blue, elevation: 4 }}> <Appbar.Header mode='center-aligned' style={{ backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.blue, elevation: 4 }}>
<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={strings('global.register')} /> <Appbar.Content titleStyle={{ fontWeight: 'bold' }} color={colors.pureWhite} title={strings('register.title')} />
</Appbar.Header> </Appbar.Header>
<View style={{ flex: 1, backgroundColor: colors.pureWhite }}> <View style={{ flex: 1, backgroundColor: theme.colors.surface }}>
<View style={styles.container}> <View style={styles.container}>
<ScrollView style={[styles.scrollViewContainer, { backgroundColor: colors.pureWhite }]}> <ScrollView style={[styles.scrollViewContainer, { backgroundColor: theme.colors.surface }]}>
<TouchableRipple onPress={handleProjectPress}> <TouchableRipple onPress={handleProjectPress}>
<TextInput <TextInput
dense={true} dense={true}
@ -379,21 +436,8 @@ export default function DialogForm() {
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={handleOpenSheet}>
<TextInput <TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetPosition}>
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={shift ? shift : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Shift'
placeholder='Shift'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={handleOpenSheetPosition}>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense={true}
@ -407,35 +451,54 @@ export default function DialogForm() {
right={<TextInput.Icon icon="chevron-down" />} right={<TextInput.Icon icon="chevron-down" />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={handleOpenSheetArea}>
<TextInput <TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheet}>
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={area ? area : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Area'
placeholder='Area'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={handleOpenSheetPost}>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense={true}
editable={false} editable={false}
value={selectedPost ? selectedPost : ''} value={shift ? shift : ''}
outlineColor={colors.amethystSmoke} outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue} activeOutlineColor={colors.blue}
mode="outlined" mode="outlined"
label='Pos' label='Shift'
placeholder='Pos' placeholder='Shift'
right={<TextInput.Icon icon="chevron-down" />} right={<TextInput.Icon icon="chevron-down" />}
/> />
</TouchableRipple> </TouchableRipple>
<TouchableRipple rippleColor={colors.pureWhite} onPress={showExpDatePickerFunc}> {position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetArea}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={area ? area : ''}
underlineColor={theme.colors.placeholder}
activeOutlineColor={theme.colors.blue}
mode="outlined"
label='Area'
placeholder='Area'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
}
{position !== 'Danru' && position !== 'spv' &&
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={handleOpenSheetPost}>
<TextInput
style={{ marginTop: 10 }}
dense={true}
editable={false}
value={selectedPost ? selectedPost : ''}
outlineColor={colors.amethystSmoke}
activeOutlineColor={colors.blue}
mode="outlined"
label='Pos'
placeholder='Pos'
right={<TextInput.Icon icon="chevron-down" />}
/>
</TouchableRipple>
}
<TouchableRipple rippleColor={isDarkTheme ? theme.colors.blue : theme.colors.pureWhite} onPress={showExpDatePickerFunc}>
<TextInput <TextInput
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
dense={true} dense={true}
@ -448,7 +511,7 @@ export default function DialogForm() {
placeholder='Masa Berlaku KTA' placeholder='Masa Berlaku KTA'
/> />
</TouchableRipple> </TouchableRipple>
{renderImages} {image !== 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}>
@ -457,11 +520,11 @@ export default function DialogForm() {
</View> </View>
</View > </View >
<View style={styles.buttonContainer}> <View style={[styles.buttonContainer, { backgroundColor: theme.colors.surface }]}>
<Button mode="outlined" style={styles.button} textColor={colors.mistBlue} onPress={() => { navigation.goBack() }} > <Button mode="outlined" style={styles.button} textColor={isDarkTheme ? theme.colors.blue : theme.colors.mistBlue} onPress={() => { navigation.goBack() }} >
Kembali Kembali
</Button> </Button>
<Button mode="contained" style={[styles.button, { backgroundColor: colors.blue }]} onPress={() => console.log('Handle Saved')}> <Button mode="contained" style={[styles.button, { backgroundColor: colors.blue }]} textColor={theme.colors.pureWhite} onPress={handleSendRegisterData}>
Simpan Simpan
</Button> </Button>
</View> </View>
@ -474,9 +537,14 @@ export default function DialogForm() {
snapPoints={['30%']} snapPoints={['30%']}
bottomInset={10} bottomInset={10}
detached={true} detached={true}
style={{ marginHorizontal: 15, shadowOpacity: 10 }} style={{ marginHorizontal: 15, shadowOpacity: 10, }}
> >
<BottomSheetView > <BottomSheetView style={{
backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite, position: "absolute", top: 0,
bottom: 0,
right: 0,
left: 0,
}}>
<TouchableRipple onPress={() => handleSelectPosition("Guard")}> <TouchableRipple onPress={() => handleSelectPosition("Guard")}>
<List.Item <List.Item
title="GUARD" title="GUARD"
@ -504,9 +572,14 @@ export default function DialogForm() {
snapPoints={['30%']} snapPoints={['30%']}
bottomInset={10} bottomInset={10}
detached={true} detached={true}
style={{ marginHorizontal: 15, shadowOpacity: 10 }} style={{ marginHorizontal: 15 }}
> >
<BottomSheetView > <BottomSheetView style={{
backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite, position: "absolute", top: 0,
bottom: 0,
right: 0,
left: 0,
}}>
<TouchableRipple onPress={() => handleSelectShift("pagi")}> <TouchableRipple onPress={() => handleSelectShift("pagi")}>
<List.Item <List.Item
title="PAGI" title="PAGI"
@ -536,7 +609,12 @@ export default function DialogForm() {
detached={true} detached={true}
style={{ marginHorizontal: 15 }} style={{ marginHorizontal: 15 }}
> >
<BottomSheetView > <BottomSheetView style={{
backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite, position: "absolute", top: 0,
bottom: 0,
right: 0,
left: 0,
}}>
{renderArea} {renderArea}
</BottomSheetView> </BottomSheetView>
</BottomSheetModal> </BottomSheetModal>
@ -549,7 +627,12 @@ export default function DialogForm() {
detached={true} detached={true}
style={{ marginHorizontal: 15 }} style={{ marginHorizontal: 15 }}
> >
<BottomSheetView > <BottomSheetView style={{
backgroundColor: isDarkTheme ? theme.colors.surface : theme.colors.pureWhite, position: "absolute", top: 0,
bottom: 0,
right: 0,
left: 0,
}}>
{renderPost} {renderPost}
</BottomSheetView> </BottomSheetView>
</BottomSheetModal> </BottomSheetModal>
@ -611,7 +694,6 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
padding: 10, padding: 10,
paddingBottom: 20, paddingBottom: 20,
backgroundColor: colors.pureWhite,
}, },
imageBlock: { imageBlock: {
marginTop: 10, marginTop: 10,

Loading…
Cancel
Save