409 lines
11 KiB
TypeScript
409 lines
11 KiB
TypeScript
"use client"
|
|
|
|
/*
|
|
* File: DocumentUploadScreen.tsx
|
|
* Description: Image upload screen for onboarding flow with react-native-image-picker.
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import type React from "react"
|
|
import { useState } from "react"
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TouchableWithoutFeedback,
|
|
Keyboard,
|
|
TouchableOpacity,
|
|
Alert,
|
|
PermissionsAndroid,
|
|
Platform,
|
|
Image
|
|
} from "react-native"
|
|
import {
|
|
launchImageLibrary,
|
|
launchCamera,
|
|
ImagePickerResponse,
|
|
MediaType,
|
|
ImagePickerOptions
|
|
} from 'react-native-image-picker'
|
|
import { Button } from "../../../../shared/src/components/Button"
|
|
import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
|
|
import { showError, showSuccess } from "../../../../shared/src/utils/helpers/Toast"
|
|
import Icon from "react-native-vector-icons/Feather"
|
|
import OnboardingContainer from "./OnboardingContainer"
|
|
import IconContainer from "./IconContainer"
|
|
import { HospitalUploadSVG } from "../../../../shared/src/components/Icons/SvgIcons"
|
|
|
|
interface SignUpData {
|
|
email: string
|
|
password: string
|
|
first_name: string
|
|
last_name: string
|
|
username:string
|
|
id_photo_url: {}
|
|
hospital_id: string
|
|
}
|
|
interface DocumentUploadScreenProps {
|
|
onContinue: (imageData: {
|
|
uri: string
|
|
name: string
|
|
type: string
|
|
size?: number
|
|
}) => void
|
|
onBack: () => void
|
|
Data:SignUpData
|
|
}
|
|
|
|
interface ImageData {
|
|
uri: string
|
|
name: string
|
|
type: string
|
|
size?: number
|
|
}
|
|
|
|
const DocumentUploadScreen: React.FC<DocumentUploadScreenProps> = ({ onContinue, onBack ,Data }) => {
|
|
const [selectedImage, setSelectedImage] = useState<ImageData | null >(Data.id_photo_url)
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
// Request camera permission for Android
|
|
const requestCameraPermission = async (): Promise<boolean> => {
|
|
if (Platform.OS === 'android') {
|
|
try {
|
|
const granted = await PermissionsAndroid.request(
|
|
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
{
|
|
title: 'Camera Permission',
|
|
message: 'This app needs camera permission to capture images.',
|
|
buttonNeutral: 'Ask Me Later',
|
|
buttonNegative: 'Cancel',
|
|
buttonPositive: 'OK',
|
|
}
|
|
)
|
|
return granted === PermissionsAndroid.RESULTS.GRANTED
|
|
} catch (err) {
|
|
console.warn(err)
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
const handleImagePicker = () => {
|
|
Alert.alert("Select Image", "Choose how you want to upload your image", [
|
|
{
|
|
text: "Camera",
|
|
onPress: () => handleCameraCapture(),
|
|
},
|
|
{
|
|
text: "Gallery",
|
|
onPress: () => handleGalleryPicker(),
|
|
},
|
|
{
|
|
text: "Cancel",
|
|
style: "cancel",
|
|
},
|
|
])
|
|
}
|
|
|
|
const handleCameraCapture = async () => {
|
|
const hasPermission = await requestCameraPermission()
|
|
|
|
if (!hasPermission) {
|
|
showError("Permission Error", "Camera permission is required to capture images.")
|
|
return
|
|
}
|
|
|
|
const options: ImagePickerOptions = {
|
|
mediaType: 'photo' as MediaType,
|
|
quality: 0.8,
|
|
maxWidth: 2000,
|
|
maxHeight: 2000,
|
|
includeBase64: false,
|
|
}
|
|
|
|
launchCamera(options, (response: ImagePickerResponse) => {
|
|
if (response.didCancel) {
|
|
return
|
|
}
|
|
|
|
if (response.errorMessage) {
|
|
showError("Camera Error", response.errorMessage)
|
|
return
|
|
}
|
|
|
|
if (response.assets && response.assets[0]) {
|
|
const asset = response.assets[0]
|
|
const imageData: ImageData = {
|
|
uri: asset.uri!,
|
|
name: asset.fileName || `image_${Date.now()}.jpg`,
|
|
type: asset.type || 'image/jpeg',
|
|
size: asset.fileSize,
|
|
}
|
|
|
|
setSelectedImage(imageData)
|
|
showSuccess("Success", "Image captured successfully!")
|
|
}
|
|
})
|
|
}
|
|
|
|
const handleGalleryPicker = () => {
|
|
const options: ImagePickerOptions = {
|
|
mediaType: 'photo' as MediaType,
|
|
quality: 0.8,
|
|
maxWidth: 2000,
|
|
maxHeight: 2000,
|
|
includeBase64: false,
|
|
}
|
|
|
|
launchImageLibrary(options, (response: ImagePickerResponse) => {
|
|
if (response.didCancel) {
|
|
return
|
|
}
|
|
|
|
if (response.errorMessage) {
|
|
showError("Gallery Error", response.errorMessage)
|
|
return
|
|
}
|
|
|
|
if (response.assets && response.assets[0]) {
|
|
const asset = response.assets[0]
|
|
const imageData: ImageData = {
|
|
uri: asset.uri!,
|
|
name: asset.fileName || `image_${Date.now()}.jpg`,
|
|
type: asset.type || 'image/jpeg',
|
|
size: asset.fileSize,
|
|
}
|
|
|
|
setSelectedImage(imageData)
|
|
showSuccess("Success", "Image selected from gallery!")
|
|
}
|
|
})
|
|
}
|
|
|
|
const formatFileSize = (bytes?: number): string => {
|
|
if (!bytes) return ''
|
|
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
if (bytes === 0) return '0 Bytes'
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
|
|
}
|
|
|
|
const getFileTypeDisplay = (type: string): string => {
|
|
if (type.includes('jpeg') || type.includes('jpg')) return 'JPEG'
|
|
if (type.includes('png')) return 'PNG'
|
|
if (type.includes('gif')) return 'GIF'
|
|
if (type.includes('webp')) return 'WebP'
|
|
return 'Image'
|
|
}
|
|
|
|
const handleContinue = () => {
|
|
if (!selectedImage) {
|
|
showError("Validation Error", "Please upload an image to continue.")
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
setTimeout(() => {
|
|
setLoading(false)
|
|
onContinue(selectedImage)
|
|
}, 1000)
|
|
}
|
|
|
|
return (
|
|
<OnboardingContainer onBack={onBack}>
|
|
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
|
<View style={styles.container}>
|
|
<View style={styles.content}>
|
|
<IconContainer Icon={()=><HospitalUploadSVG/>} />
|
|
|
|
<Text style={styles.title}>Upload Image</Text>
|
|
<Text style={styles.subtitle}>Please upload your profile picture or identification image.</Text>
|
|
|
|
<TouchableOpacity style={styles.uploadContainer} onPress={handleImagePicker}>
|
|
<View style={styles.uploadContent}>
|
|
{selectedImage ? (
|
|
<View style={styles.imagePreviewContainer}>
|
|
<Image source={{ uri: selectedImage.uri }} style={styles.imagePreview} />
|
|
<TouchableOpacity style={styles.imageOverlay} onPress={()=>setSelectedImage(null)}>
|
|
<Icon name="x" size={24} color={'#FFFFFF'} />
|
|
</TouchableOpacity>
|
|
<Text style={styles.uploadedText}>Image Uploaded</Text>
|
|
<Text style={styles.fileName}>{selectedImage.name}</Text>
|
|
<View style={styles.fileDetails}>
|
|
<Text style={styles.fileType}>{getFileTypeDisplay(selectedImage.type)}</Text>
|
|
{selectedImage.size && (
|
|
<Text style={styles.fileSize}> • {formatFileSize(selectedImage.size)}</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
) : (
|
|
<>
|
|
<Icon name="image" size={48} color={Colors.textSecondary} />
|
|
<Text style={styles.uploadText}>Tap to upload image</Text>
|
|
<Text style={styles.uploadSubtext}>JPG, PNG supported</Text>
|
|
</>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
{selectedImage && (
|
|
<TouchableOpacity style={styles.changeButton} onPress={handleImagePicker}>
|
|
<Text style={styles.changeButtonText}>Change Image</Text>
|
|
</TouchableOpacity>
|
|
)}
|
|
|
|
<Button
|
|
title="Continue"
|
|
onPress={handleContinue}
|
|
style={[styles.button, !selectedImage && styles.buttonDisabled]}
|
|
loading={loading}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</TouchableWithoutFeedback>
|
|
</OnboardingContainer>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
},
|
|
iconContainer: {
|
|
alignItems: "center",
|
|
marginBottom: Spacing.xl,
|
|
},
|
|
iconWrapper: {
|
|
width: 80,
|
|
height: 80,
|
|
borderRadius: 20,
|
|
backgroundColor: Colors.backgroundAlt,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
borderWidth: 1,
|
|
borderColor: Colors.border,
|
|
},
|
|
title: {
|
|
fontFamily: Typography.fontFamily.bold,
|
|
fontSize: Typography.fontSize.title,
|
|
color: Colors.textPrimary,
|
|
textAlign: "center",
|
|
marginBottom: Spacing.sm,
|
|
},
|
|
subtitle: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.md,
|
|
color: Colors.textSecondary,
|
|
textAlign: "center",
|
|
marginBottom: Spacing.xl,
|
|
},
|
|
uploadContainer: {
|
|
backgroundColor: Colors.backgroundAlt,
|
|
borderWidth: 2,
|
|
borderColor: Colors.border,
|
|
borderStyle: "dashed",
|
|
borderRadius: 12,
|
|
padding: Spacing.xl,
|
|
marginBottom: Spacing.md,
|
|
minHeight: 200,
|
|
},
|
|
uploadContent: {
|
|
alignItems: "center",
|
|
},
|
|
imagePreviewContainer: {
|
|
alignItems: "center",
|
|
width: '100%',
|
|
},
|
|
imagePreview: {
|
|
width: '100%',
|
|
height: 150,
|
|
borderRadius: 12,
|
|
marginBottom: Spacing.sm,
|
|
resizeMode: 'contain',
|
|
},
|
|
imageOverlay: {
|
|
position: 'absolute',
|
|
top: 0,
|
|
right: 20,
|
|
marginRight: -20, // Half of image width minus half of overlay
|
|
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
|
borderRadius: 17,
|
|
width: 34,
|
|
height: 34,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding:2
|
|
},
|
|
uploadText: {
|
|
fontFamily: Typography.fontFamily.medium,
|
|
fontSize: Typography.fontSize.md,
|
|
color: Colors.textPrimary,
|
|
marginTop: Spacing.sm,
|
|
},
|
|
uploadSubtext: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.sm,
|
|
color: Colors.textSecondary,
|
|
marginTop: Spacing.xs,
|
|
},
|
|
uploadedText: {
|
|
fontFamily: Typography.fontFamily.medium,
|
|
fontSize: Typography.fontSize.md,
|
|
color: Colors.success,
|
|
marginTop: Spacing.sm,
|
|
},
|
|
fileName: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.sm,
|
|
color: Colors.textSecondary,
|
|
marginTop: Spacing.xs,
|
|
textAlign: "center",
|
|
maxWidth: '80%',
|
|
},
|
|
fileDetails: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
marginTop: Spacing.xs,
|
|
},
|
|
fileType: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.xs,
|
|
color: Colors.textMuted,
|
|
},
|
|
fileSize: {
|
|
fontFamily: Typography.fontFamily.regular,
|
|
fontSize: Typography.fontSize.xs,
|
|
color: Colors.textMuted,
|
|
},
|
|
changeButton: {
|
|
alignSelf: "center",
|
|
marginBottom: Spacing.xl,
|
|
},
|
|
changeButtonText: {
|
|
fontFamily: Typography.fontFamily.medium,
|
|
fontSize: Typography.fontSize.md,
|
|
color: Colors.primary,
|
|
},
|
|
button: {
|
|
marginTop: Spacing.md,
|
|
},
|
|
buttonDisabled: {
|
|
opacity: 0.5,
|
|
},
|
|
})
|
|
|
|
export default DocumentUploadScreen
|
|
|
|
/*
|
|
* End of File: DocumentUploadScreen.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/ |