react-native-biometric-verifier 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/package.json +32 -0
- package/src/Asset/Icons/Female.png +0 -0
- package/src/Asset/Icons/FemaleI(1).png +0 -0
- package/src/Asset/Icons/Male(2).png +0 -0
- package/src/Asset/Icons/arrowleft.png +0 -0
- package/src/Asset/Icons/barcode2.png +0 -0
- package/src/Asset/Icons/calendar.png +0 -0
- package/src/Asset/Icons/downarrow.png +0 -0
- package/src/Asset/Icons/male.png +0 -0
- package/src/Asset/Icons/stethoscope.jpg +0 -0
- package/src/Asset/Icons/user.png +0 -0
- package/src/Asset/Icons/youtube.png +0 -0
- package/src/Asset/gif/Pulse.gif +0 -0
- package/src/Asset/gif/Search_blue.gif +0 -0
- package/src/Asset/gif/hb.gif +0 -0
- package/src/Asset/gif/heartpulse.gif +0 -0
- package/src/Asset/gif/lab_1.gif +0 -0
- package/src/Asset/gif/loader.gif +0 -0
- package/src/Asset/gif/overwrite.gif +0 -0
- package/src/Asset/gif/pharmacist.gif +0 -0
- package/src/Asset/images/AdamsNeilson.jpg +0 -0
- package/src/Asset/images/AyurvedicHospital.jpg +0 -0
- package/src/Asset/images/DevamathaLogo.jpg +0 -0
- package/src/Asset/images/IMA.png +0 -0
- package/src/Asset/images/amalainstitute.png +0 -0
- package/src/Asset/images/amalainstituteN.png +0 -0
- package/src/Asset/images/amalainstituteX.png +0 -0
- package/src/Asset/images/amalalogo.jpg +0 -0
- package/src/Asset/images/amalalogoN.jpg +0 -0
- package/src/Asset/images/arrow-collapse.png +0 -0
- package/src/Asset/images/arrow-expand.png +0 -0
- package/src/Asset/images/audio.png +0 -0
- package/src/Asset/images/camera.png +0 -0
- package/src/Asset/images/camera_red.png +0 -0
- package/src/Asset/images/companyaddress_transparent.png +0 -0
- package/src/Asset/images/companyname_transparent.png +0 -0
- package/src/Asset/images/download.png +0 -0
- package/src/Asset/images/jes.png +0 -0
- package/src/Asset/images/lefteye.jpg +0 -0
- package/src/Asset/images/mundakayamlogo.png +0 -0
- package/src/Asset/images/nirmalanabh.jpeg +0 -0
- package/src/Asset/images/pregnantlady.png +0 -0
- package/src/Asset/images/prescriptioneye.png +0 -0
- package/src/Asset/images/rblogo.png +0 -0
- package/src/Asset/images/righteye.jpg +0 -0
- package/src/Asset/images/rtlogo.png +0 -0
- package/src/Asset/images/video_red.png +0 -0
- package/src/Asset/images/vimaljyothi.png +0 -0
- package/src/components/CountdownTimer.js +42 -0
- package/src/components/EmployeeCard.js +61 -0
- package/src/components/Notification.js +63 -0
- package/src/components/StateIndicator.js +112 -0
- package/src/components/styles.js +182 -0
- package/src/hooks/useCountdown.js +72 -0
- package/src/hooks/useGeolocation.js +72 -0
- package/src/hooks/useImageProcessing.js +79 -0
- package/src/hooks/useNotifyMessage.js +131 -0
- package/src/index.js +283 -0
- package/src/utils/Global.js +6 -0
- package/src/utils/NetworkServiceCall.js +35 -0
- package/src/utils/constants.js +69 -0
- package/src/utils/distanceCalculator.js +25 -0
- package/src/utils/logger.js +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# React Native Biometric Verifier
|
|
2
|
+
|
|
3
|
+
A comprehensive biometric verification module for React Native that combines face recognition and QR code scanning with location verification.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Face recognition verification
|
|
8
|
+
- QR code scanning
|
|
9
|
+
- Location verification
|
|
10
|
+
- Animated UI with state indicators
|
|
11
|
+
- Countdown timer
|
|
12
|
+
- Customizable notification system
|
|
13
|
+
- Comprehensive error handling
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install react-native-biometric-verifier
|
|
19
|
+
# or
|
|
20
|
+
yarn add react-native-biometric-verifier
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-biometric-verifier",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A React Native module for biometric verification with face recognition and QR code scanning",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react-native",
|
|
11
|
+
"biometric",
|
|
12
|
+
"verification",
|
|
13
|
+
"face-recognition",
|
|
14
|
+
"qr-code"
|
|
15
|
+
],
|
|
16
|
+
"author": "JESCON",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
20
|
+
"react-native": ">=0.60.0",
|
|
21
|
+
"react-native-vector-icons": "^9.0.0",
|
|
22
|
+
"react-native-geolocation-service": "^5.0.0",
|
|
23
|
+
"react-native-image-resizer": "^1.0.0",
|
|
24
|
+
"@react-navigation/native": "^6.0.0",
|
|
25
|
+
"prop-types": "^15.8.0",
|
|
26
|
+
"react-native-fs": "^2.20.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"react": "^18.0.0",
|
|
30
|
+
"react-native": "^0.69.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, Animated, StyleSheet } from 'react-native';
|
|
3
|
+
import { COLORS } from '../utils/constants';
|
|
4
|
+
import { styles } from './styles';
|
|
5
|
+
|
|
6
|
+
export const CountdownTimer = ({ duration, currentTime }) => {
|
|
7
|
+
const progress = React.useRef(new Animated.Value(1)).current;
|
|
8
|
+
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
Animated.timing(progress, {
|
|
11
|
+
toValue: currentTime / duration,
|
|
12
|
+
duration: 1000,
|
|
13
|
+
useNativeDriver: false,
|
|
14
|
+
}).start();
|
|
15
|
+
}, [currentTime, duration, progress]);
|
|
16
|
+
|
|
17
|
+
const circumference = 2 * Math.PI * 40;
|
|
18
|
+
const strokeDashoffset = progress.interpolate({
|
|
19
|
+
inputRange: [0, 1],
|
|
20
|
+
outputRange: [circumference, 0],
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<View style={styles.timerContainer}>
|
|
25
|
+
<View style={styles.timerCircle}>
|
|
26
|
+
<Animated.View style={styles.timerProgressContainer}>
|
|
27
|
+
<Animated.View
|
|
28
|
+
style={[
|
|
29
|
+
styles.timerProgress,
|
|
30
|
+
{
|
|
31
|
+
strokeDashoffset,
|
|
32
|
+
transform: [{ rotate: '-90deg' }],
|
|
33
|
+
}
|
|
34
|
+
]}
|
|
35
|
+
/>
|
|
36
|
+
</Animated.View>
|
|
37
|
+
<Text style={styles.timerText}>{currentTime}s</Text>
|
|
38
|
+
</View>
|
|
39
|
+
<Text style={styles.timerLabel}>Remaining</Text>
|
|
40
|
+
</View>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, Image } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { COLORS ,IMAGE_URL} from '../utils/constants';
|
|
6
|
+
import { styles } from './styles';
|
|
7
|
+
|
|
8
|
+
export const EmployeeCard = ({ employeeData }) => {
|
|
9
|
+
const [imageError, setImageError] = useState(false);
|
|
10
|
+
|
|
11
|
+
if (!employeeData || typeof employeeData !== 'object') {
|
|
12
|
+
console.warn('EmployeeCard: Invalid or missing employeeData');
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { employeename, employeeid, imageurl } = employeeData;
|
|
17
|
+
|
|
18
|
+
const employeeName = employeename || 'Unknown Employee';
|
|
19
|
+
const employeeId = employeeid || 'N/A';
|
|
20
|
+
const imageSource = !imageError && imageurl
|
|
21
|
+
? { uri: `${IMAGE_URL}${imageurl}` }
|
|
22
|
+
: require('../Asset/images/camera.png'); // Add a local fallback image in assets
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<View style={styles.employeeCard}>
|
|
26
|
+
{/* Employee Image */}
|
|
27
|
+
<View style={styles.employeeImageContainer}>
|
|
28
|
+
<Image
|
|
29
|
+
source={imageSource}
|
|
30
|
+
style={styles.employeeImage}
|
|
31
|
+
resizeMode="cover"
|
|
32
|
+
onError={() => {
|
|
33
|
+
console.error(`Error loading image for employee: ${employeeName}`);
|
|
34
|
+
setImageError(true);
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
<View style={styles.employeeImageOverlay} />
|
|
38
|
+
</View>
|
|
39
|
+
|
|
40
|
+
{/* Employee Info */}
|
|
41
|
+
<Text style={styles.empName}>{employeeName}</Text>
|
|
42
|
+
<Text style={styles.empId}>Employee ID: {employeeId}</Text>
|
|
43
|
+
|
|
44
|
+
{/* Verified Badge */}
|
|
45
|
+
<View style={styles.employeeDetails}>
|
|
46
|
+
<View style={styles.detailItem}>
|
|
47
|
+
<Icon name="verified-user" size={16} color={COLORS.success} />
|
|
48
|
+
<Text style={styles.detailText}>Identity Verified</Text>
|
|
49
|
+
</View>
|
|
50
|
+
</View>
|
|
51
|
+
</View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
EmployeeCard.propTypes = {
|
|
56
|
+
employeeData: PropTypes.shape({
|
|
57
|
+
employeename: PropTypes.string,
|
|
58
|
+
employeeid: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
59
|
+
imageurl: PropTypes.string,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Animated, Text } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { COLORS } from '../utils/constants';
|
|
6
|
+
import { styles } from './styles';
|
|
7
|
+
|
|
8
|
+
export const Notification = ({ notification, fadeAnim, slideAnim }) => {
|
|
9
|
+
// Defensive check for null or malformed props
|
|
10
|
+
if (
|
|
11
|
+
!notification ||
|
|
12
|
+
typeof notification !== 'object' ||
|
|
13
|
+
!notification.visible
|
|
14
|
+
) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { type = 'info', message = '' } = notification;
|
|
19
|
+
|
|
20
|
+
// Icon and color mapping
|
|
21
|
+
const iconMap = {
|
|
22
|
+
success: { name: 'check-circle', color: COLORS.success },
|
|
23
|
+
error: { name: 'error', color: COLORS.error },
|
|
24
|
+
info: { name: 'info', color: COLORS.info },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const { name: iconName, color: iconColor } = iconMap[type] || iconMap.info;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Animated.View
|
|
31
|
+
style={[
|
|
32
|
+
styles.notificationBox,
|
|
33
|
+
styles[type] || {}, // Prevent undefined style
|
|
34
|
+
{
|
|
35
|
+
opacity: fadeAnim instanceof Animated.Value ? fadeAnim : 1,
|
|
36
|
+
transform: [
|
|
37
|
+
{
|
|
38
|
+
translateY: slideAnim instanceof Animated.Value ? slideAnim : 0,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
44
|
+
<Icon
|
|
45
|
+
name={iconName}
|
|
46
|
+
size={20}
|
|
47
|
+
color={iconColor}
|
|
48
|
+
style={styles.notificationIcon}
|
|
49
|
+
/>
|
|
50
|
+
<Text style={styles.notificationText}>{message}</Text>
|
|
51
|
+
</Animated.View>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
Notification.propTypes = {
|
|
56
|
+
notification: PropTypes.shape({
|
|
57
|
+
visible: PropTypes.bool.isRequired,
|
|
58
|
+
type: PropTypes.oneOf(['success', 'error', 'info']),
|
|
59
|
+
message: PropTypes.string,
|
|
60
|
+
}),
|
|
61
|
+
fadeAnim: PropTypes.any, // Animated.Value expected
|
|
62
|
+
slideAnim: PropTypes.any, // Animated.Value expected
|
|
63
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, ActivityIndicator, Easing } from 'react-native';
|
|
3
|
+
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
|
+
import { ANIMATION_STATES, COLORS } from '../utils/constants';
|
|
5
|
+
import { styles } from './styles';
|
|
6
|
+
|
|
7
|
+
export const StateIndicator = ({ state, size = 100 }) => {
|
|
8
|
+
const spinValue = useRef(new Animated.Value(0)).current;
|
|
9
|
+
const pulseValue = useRef(new Animated.Value(1)).current;
|
|
10
|
+
const spinAnimation = useRef(null);
|
|
11
|
+
const pulseAnimation = useRef(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Stop ongoing animations when state changes
|
|
15
|
+
spinValue.stopAnimation();
|
|
16
|
+
pulseValue.stopAnimation();
|
|
17
|
+
|
|
18
|
+
if (
|
|
19
|
+
state === ANIMATION_STATES.PROCESSING ||
|
|
20
|
+
state === ANIMATION_STATES.FACE_SCAN ||
|
|
21
|
+
state === ANIMATION_STATES.QR_SCAN
|
|
22
|
+
) {
|
|
23
|
+
spinAnimation.current = Animated.loop(
|
|
24
|
+
Animated.timing(spinValue, {
|
|
25
|
+
toValue: 1,
|
|
26
|
+
duration: 1500,
|
|
27
|
+
easing: Easing.linear,
|
|
28
|
+
useNativeDriver: true,
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
spinAnimation.current.start();
|
|
32
|
+
}
|
|
33
|
+
else if (
|
|
34
|
+
state === ANIMATION_STATES.SUCCESS ||
|
|
35
|
+
state === ANIMATION_STATES.ERROR
|
|
36
|
+
) {
|
|
37
|
+
pulseAnimation.current = Animated.loop(
|
|
38
|
+
Animated.sequence([
|
|
39
|
+
Animated.timing(pulseValue, {
|
|
40
|
+
toValue: 1.1,
|
|
41
|
+
duration: 500,
|
|
42
|
+
useNativeDriver: true,
|
|
43
|
+
}),
|
|
44
|
+
Animated.timing(pulseValue, {
|
|
45
|
+
toValue: 1,
|
|
46
|
+
duration: 500,
|
|
47
|
+
useNativeDriver: true,
|
|
48
|
+
}),
|
|
49
|
+
]),
|
|
50
|
+
{ iterations: 2 }
|
|
51
|
+
);
|
|
52
|
+
pulseAnimation.current.start();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
spinValue.setValue(0);
|
|
56
|
+
pulseValue.setValue(1);
|
|
57
|
+
}
|
|
58
|
+
}, [state, spinValue, pulseValue]);
|
|
59
|
+
|
|
60
|
+
const spin = spinValue.interpolate({
|
|
61
|
+
inputRange: [0, 1],
|
|
62
|
+
outputRange: ['0deg', '360deg'],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const getIcon = () => {
|
|
66
|
+
switch (state) {
|
|
67
|
+
case ANIMATION_STATES.FACE_SCAN:
|
|
68
|
+
return <Icon name="face" size={size * 0.5} color={COLORS.primary} />;
|
|
69
|
+
case ANIMATION_STATES.QR_SCAN:
|
|
70
|
+
return <Icon name="qr-code-scanner" size={size * 0.5} color={COLORS.primary} />;
|
|
71
|
+
case ANIMATION_STATES.PROCESSING:
|
|
72
|
+
return <Icon name="settings" size={size * 0.5} color={COLORS.info} />;
|
|
73
|
+
case ANIMATION_STATES.SUCCESS:
|
|
74
|
+
return <Icon name="check-circle" size={size * 0.5} color={COLORS.success} />;
|
|
75
|
+
case ANIMATION_STATES.ERROR:
|
|
76
|
+
return <Icon name="error" size={size * 0.5} color={COLORS.error} />;
|
|
77
|
+
default:
|
|
78
|
+
return <Icon name="hourglass-empty" size={size * 0.5} color={COLORS.gray} />;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Animated.View
|
|
84
|
+
style={[
|
|
85
|
+
styles.indicatorContainer,
|
|
86
|
+
{
|
|
87
|
+
width: size,
|
|
88
|
+
height: size,
|
|
89
|
+
transform: [
|
|
90
|
+
{ scale: pulseValue },
|
|
91
|
+
...(state === ANIMATION_STATES.PROCESSING ||
|
|
92
|
+
state === ANIMATION_STATES.FACE_SCAN ||
|
|
93
|
+
state === ANIMATION_STATES.QR_SCAN
|
|
94
|
+
? [{ rotate: spin }]
|
|
95
|
+
: []),
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
]}
|
|
99
|
+
>
|
|
100
|
+
{(state === ANIMATION_STATES.PROCESSING ||
|
|
101
|
+
state === ANIMATION_STATES.FACE_SCAN ||
|
|
102
|
+
state === ANIMATION_STATES.QR_SCAN) ? (
|
|
103
|
+
<ActivityIndicator
|
|
104
|
+
size="large"
|
|
105
|
+
color={state === ANIMATION_STATES.PROCESSING ? COLORS.info : COLORS.primary}
|
|
106
|
+
/>
|
|
107
|
+
) : (
|
|
108
|
+
getIcon()
|
|
109
|
+
)}
|
|
110
|
+
</Animated.View>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { StyleSheet, Platform } from 'react-native';
|
|
2
|
+
import { COLORS } from '../utils/constants';
|
|
3
|
+
|
|
4
|
+
export const styles = StyleSheet.create({
|
|
5
|
+
modalBg: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
8
|
+
justifyContent: 'center',
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
paddingHorizontal: 20,
|
|
11
|
+
},
|
|
12
|
+
close: {
|
|
13
|
+
position: 'absolute',
|
|
14
|
+
top: 20,
|
|
15
|
+
right: 20,
|
|
16
|
+
zIndex: 10,
|
|
17
|
+
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
18
|
+
borderRadius: 20,
|
|
19
|
+
padding: 5,
|
|
20
|
+
},
|
|
21
|
+
title: {
|
|
22
|
+
fontSize: 26,
|
|
23
|
+
fontWeight: '700',
|
|
24
|
+
color: COLORS.light,
|
|
25
|
+
marginBottom: 5,
|
|
26
|
+
textAlign: 'center',
|
|
27
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
28
|
+
},
|
|
29
|
+
subTitle: {
|
|
30
|
+
fontSize: 18,
|
|
31
|
+
color: COLORS.light,
|
|
32
|
+
marginBottom: 25,
|
|
33
|
+
fontWeight: '600',
|
|
34
|
+
textAlign: 'center',
|
|
35
|
+
opacity: 0.9,
|
|
36
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif-medium',
|
|
37
|
+
},
|
|
38
|
+
indicatorContainer: {
|
|
39
|
+
justifyContent: 'center',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
marginVertical: 25,
|
|
42
|
+
},
|
|
43
|
+
employeeCard: {
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
marginVertical: 20,
|
|
46
|
+
width: '100%',
|
|
47
|
+
padding: 20,
|
|
48
|
+
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
49
|
+
borderRadius: 16,
|
|
50
|
+
shadowColor: '#000',
|
|
51
|
+
shadowOffset: { width: 0, height: 4 },
|
|
52
|
+
shadowOpacity: 0.1,
|
|
53
|
+
shadowRadius: 6,
|
|
54
|
+
elevation: 3,
|
|
55
|
+
},
|
|
56
|
+
employeeImageContainer: {
|
|
57
|
+
width: 100,
|
|
58
|
+
height: 100,
|
|
59
|
+
borderRadius: 50,
|
|
60
|
+
borderWidth: 3,
|
|
61
|
+
borderColor: COLORS.primary,
|
|
62
|
+
position: 'relative',
|
|
63
|
+
overflow: 'hidden',
|
|
64
|
+
},
|
|
65
|
+
employeeImage: {
|
|
66
|
+
width: '100%',
|
|
67
|
+
height: '100%',
|
|
68
|
+
},
|
|
69
|
+
employeeImageOverlay: {
|
|
70
|
+
...StyleSheet.absoluteFillObject,
|
|
71
|
+
backgroundColor: 'rgba(108, 99, 255, 0.1)',
|
|
72
|
+
},
|
|
73
|
+
empName: {
|
|
74
|
+
fontSize: 20,
|
|
75
|
+
fontWeight: '600',
|
|
76
|
+
marginTop: 15,
|
|
77
|
+
color: COLORS.dark,
|
|
78
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif-medium',
|
|
79
|
+
},
|
|
80
|
+
empId: {
|
|
81
|
+
fontSize: 15,
|
|
82
|
+
color: COLORS.gray,
|
|
83
|
+
marginTop: 5,
|
|
84
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
85
|
+
},
|
|
86
|
+
employeeDetails: {
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
marginTop: 15,
|
|
89
|
+
justifyContent: 'center',
|
|
90
|
+
flexWrap: 'wrap',
|
|
91
|
+
},
|
|
92
|
+
detailItem: {
|
|
93
|
+
flexDirection: 'row',
|
|
94
|
+
alignItems: 'center',
|
|
95
|
+
marginHorizontal: 8,
|
|
96
|
+
marginVertical: 4,
|
|
97
|
+
},
|
|
98
|
+
detailText: {
|
|
99
|
+
fontSize: 13,
|
|
100
|
+
color: COLORS.gray,
|
|
101
|
+
marginLeft: 5,
|
|
102
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
103
|
+
},
|
|
104
|
+
notificationBox: {
|
|
105
|
+
padding: 15,
|
|
106
|
+
borderRadius: 12,
|
|
107
|
+
marginVertical: 10,
|
|
108
|
+
width: '100%',
|
|
109
|
+
flexDirection: 'row',
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
shadowColor: '#000',
|
|
112
|
+
shadowOffset: { width: 0, height: 2 },
|
|
113
|
+
shadowOpacity: 0.1,
|
|
114
|
+
shadowRadius: 4,
|
|
115
|
+
elevation: 2,
|
|
116
|
+
},
|
|
117
|
+
info: {
|
|
118
|
+
backgroundColor: 'rgba(33, 150, 243, 0.15)',
|
|
119
|
+
borderLeftWidth: 4,
|
|
120
|
+
borderLeftColor: COLORS.light,
|
|
121
|
+
},
|
|
122
|
+
success: {
|
|
123
|
+
backgroundColor: 'rgba(76, 175, 80, 0.15)',
|
|
124
|
+
borderLeftWidth: 4,
|
|
125
|
+
borderLeftColor: COLORS.success,
|
|
126
|
+
},
|
|
127
|
+
error: {
|
|
128
|
+
backgroundColor: 'rgba(244, 67, 54, 0.15)',
|
|
129
|
+
borderLeftWidth: 4,
|
|
130
|
+
borderLeftColor: COLORS.error,
|
|
131
|
+
},
|
|
132
|
+
notificationIcon: {
|
|
133
|
+
marginRight: 10,
|
|
134
|
+
},
|
|
135
|
+
notificationText: {
|
|
136
|
+
fontSize: 14,
|
|
137
|
+
color: COLORS.light,
|
|
138
|
+
fontWeight: '500',
|
|
139
|
+
flex: 1,
|
|
140
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
141
|
+
},
|
|
142
|
+
timerContainer: {
|
|
143
|
+
marginTop: 20,
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
},
|
|
146
|
+
timerCircle: {
|
|
147
|
+
width: 80,
|
|
148
|
+
height: 80,
|
|
149
|
+
borderRadius: 40,
|
|
150
|
+
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
151
|
+
justifyContent: 'center',
|
|
152
|
+
alignItems: 'center',
|
|
153
|
+
position: 'relative',
|
|
154
|
+
},
|
|
155
|
+
timerProgressContainer: {
|
|
156
|
+
position: 'absolute',
|
|
157
|
+
width: '100%',
|
|
158
|
+
height: '100%',
|
|
159
|
+
},
|
|
160
|
+
timerProgress: {
|
|
161
|
+
width: 76,
|
|
162
|
+
height: 76,
|
|
163
|
+
borderRadius: 38,
|
|
164
|
+
borderWidth: 3,
|
|
165
|
+
borderColor: COLORS.light,
|
|
166
|
+
borderLeftColor: 'transparent',
|
|
167
|
+
borderBottomColor: 'transparent',
|
|
168
|
+
},
|
|
169
|
+
timerText: {
|
|
170
|
+
fontSize: 20,
|
|
171
|
+
fontWeight: '700',
|
|
172
|
+
color: COLORS.light,
|
|
173
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif-medium',
|
|
174
|
+
},
|
|
175
|
+
timerLabel: {
|
|
176
|
+
fontSize: 14,
|
|
177
|
+
color: COLORS.light,
|
|
178
|
+
marginTop: 5,
|
|
179
|
+
opacity: 0.8,
|
|
180
|
+
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
181
|
+
},
|
|
182
|
+
});
|