change password registerd mail id validation api integrated
This commit is contained in:
parent
29940278e6
commit
5186e39e23
@ -13,6 +13,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:theme="@style/AppTheme"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
9
android/app/src/main/res/xml/network_security_config.xml
Normal file
9
android/app/src/main/res/xml/network_security_config.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
@ -33,7 +33,18 @@ 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
|
||||
@ -42,6 +53,7 @@ interface DocumentUploadScreenProps {
|
||||
size?: number
|
||||
}) => void
|
||||
onBack: () => void
|
||||
Data:SignUpData
|
||||
}
|
||||
|
||||
interface ImageData {
|
||||
@ -51,8 +63,8 @@ interface ImageData {
|
||||
size?: number
|
||||
}
|
||||
|
||||
const DocumentUploadScreen: React.FC<DocumentUploadScreenProps> = ({ onContinue, onBack }) => {
|
||||
const [selectedImage, setSelectedImage] = useState<ImageData | null>(null)
|
||||
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
|
||||
@ -206,11 +218,7 @@ const DocumentUploadScreen: React.FC<DocumentUploadScreenProps> = ({ onContinue,
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<View style={styles.container}>
|
||||
<View style={styles.content}>
|
||||
<View style={styles.iconContainer}>
|
||||
<View style={styles.iconWrapper}>
|
||||
<Icon name="upload" size={32} color={Colors.primary} />
|
||||
</View>
|
||||
</View>
|
||||
<IconContainer Icon={()=><HospitalUploadSVG/>} />
|
||||
|
||||
<Text style={styles.title}>Upload Image</Text>
|
||||
<Text style={styles.subtitle}>Please upload your profile picture or identification image.</Text>
|
||||
|
||||
148
app/modules/Auth/components/EmailAlreadyRegisteredModal.tsx
Normal file
148
app/modules/Auth/components/EmailAlreadyRegisteredModal.tsx
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* File: EmailAlreadyRegisteredModal.tsx
|
||||
* Description: Modal popup for when email is already registered
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
Modal,
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
StyleSheet,
|
||||
Dimensions,
|
||||
} from 'react-native'
|
||||
import { Colors } from '../../../../shared/src/theme'
|
||||
|
||||
interface EmailAlreadyRegisteredModalProps {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
onGoToLogin: () => void
|
||||
}
|
||||
|
||||
const { width } = Dimensions.get('window')
|
||||
|
||||
const EmailAlreadyRegisteredModal: React.FC<EmailAlreadyRegisteredModalProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
onGoToLogin,
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={visible}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.modalContainer}>
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.title}>Email Already Registered</Text>
|
||||
<Text style={styles.message}>
|
||||
Your email is already registered. The login credentials have been sent to your email—please check your inbox to proceed.
|
||||
</Text>
|
||||
|
||||
<View style={styles.buttonContainer}>
|
||||
<TouchableOpacity
|
||||
style={[styles.button, styles.secondaryButton]}
|
||||
onPress={onClose}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={styles.secondaryButtonText}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.button, styles.primaryButton]}
|
||||
onPress={onGoToLogin}
|
||||
activeOpacity={0.8}
|
||||
>
|
||||
<Text style={styles.primaryButtonText}>Go to Login</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalContainer: {
|
||||
width: width * 0.85,
|
||||
backgroundColor: Colors.background || '#FFFFFF',
|
||||
borderRadius: 12,
|
||||
elevation: 5,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 2,
|
||||
},
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 3.84,
|
||||
},
|
||||
content: {
|
||||
padding: 24,
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: '#333333',
|
||||
textAlign: 'center',
|
||||
marginBottom: 16,
|
||||
},
|
||||
message: {
|
||||
fontSize: 16,
|
||||
color: Colors.textSecondary || '#666666',
|
||||
textAlign: 'center',
|
||||
lineHeight: 24,
|
||||
marginBottom: 24,
|
||||
},
|
||||
buttonContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
borderRadius: 8,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: 48,
|
||||
},
|
||||
primaryButton: {
|
||||
backgroundColor: Colors.primary || '#007AFF',
|
||||
},
|
||||
secondaryButton: {
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.border || '#E0E0E0',
|
||||
},
|
||||
primaryButtonText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
secondaryButtonText: {
|
||||
color: '#333333',
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
})
|
||||
|
||||
export default EmailAlreadyRegisteredModal
|
||||
|
||||
/*
|
||||
* End of File: EmailAlreadyRegisteredModal.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
@ -17,14 +17,27 @@ import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
|
||||
import { showError } from "../../../../shared/src/utils/helpers/Toast"
|
||||
import { validateEmail } from "../../../../shared/src/utils/validation/validators"
|
||||
import Icon from "react-native-vector-icons/Feather"
|
||||
import { EmailIconSVG } from "../../../../shared/src/components/Icons/SvgIcons"
|
||||
|
||||
interface SignUpDataInterface {
|
||||
email: string
|
||||
password: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
username:string
|
||||
id_photo_url: {}
|
||||
hospital_id: string
|
||||
}
|
||||
interface EmailScreenProps {
|
||||
onContinue: (email: string) => void
|
||||
onBack: () => void
|
||||
Data:SignUpDataInterface
|
||||
}
|
||||
|
||||
const EmailScreen: React.FC<EmailScreenProps> = ({ onContinue, onBack }) => {
|
||||
const [email, setEmail] = useState("")
|
||||
|
||||
const EmailScreen: React.FC<EmailScreenProps> = ({ onContinue, onBack, Data }) => {
|
||||
console.log('signupData',Data)
|
||||
const [email, setEmail] = useState(Data.email)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleContinue = () => {
|
||||
@ -40,13 +53,14 @@ const EmailScreen: React.FC<EmailScreenProps> = ({ onContinue, onBack }) => {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
onContinue(email)
|
||||
}, 1000)
|
||||
|
||||
}, 2000)
|
||||
onContinue(email)
|
||||
}
|
||||
|
||||
return (
|
||||
<OnboardingContainer onBack={onBack}>
|
||||
<IconContainer iconName="mail" />
|
||||
<IconContainer Icon={()=><EmailIconSVG/>} />
|
||||
|
||||
<Text style={styles.title}>What's your email?</Text>
|
||||
<Text style={styles.subtitle}>Please enter your email address.</Text>
|
||||
@ -88,20 +102,20 @@ const styles = StyleSheet.create({
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: Colors.inputBackground,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.border,
|
||||
borderWidth:1,
|
||||
borderRadius: 12,
|
||||
marginBottom: Spacing.xl,
|
||||
paddingHorizontal: Spacing.md,
|
||||
paddingVertical: 2,
|
||||
shadowColor: Colors.backButtonShadow,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 1,
|
||||
},
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
// shadowOffset: {
|
||||
// width: 0,
|
||||
// height: 1,
|
||||
// },
|
||||
// shadowOpacity: 0.05,
|
||||
// shadowRadius: 2,
|
||||
// elevation: 1,
|
||||
},
|
||||
inputIcon: {
|
||||
marginRight: Spacing.sm,
|
||||
|
||||
@ -18,10 +18,21 @@ import Icon from "react-native-vector-icons/Feather"
|
||||
import OnboardingContainer from "./OnboardingContainer"
|
||||
import { selectHospitals } from "../redux"
|
||||
import { useSelector } from "react-redux"
|
||||
|
||||
import IconContainer from "./IconContainer"
|
||||
import { HospitalSVG } from "../../../../shared/src/components/Icons/SvgIcons"
|
||||
interface SignUpData {
|
||||
email: string
|
||||
password: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
username:string
|
||||
id_photo_url: {}|null
|
||||
hospital_id: string
|
||||
}
|
||||
interface HospitalSelectionScreenProps {
|
||||
onContinue: (hospitalId: string) => void
|
||||
onBack: () => void
|
||||
Data: SignUpData
|
||||
}
|
||||
|
||||
const hospitalData = [
|
||||
@ -35,8 +46,8 @@ const hospitalData = [
|
||||
{ label: "Veterans Medical Center", value: "veterans" },
|
||||
]
|
||||
|
||||
const HospitalSelectionScreen: React.FC<HospitalSelectionScreenProps> = ({ onContinue, onBack }) => {
|
||||
const [selectedHospital, setSelectedHospital] = useState<string | null>(null)
|
||||
const HospitalSelectionScreen: React.FC<HospitalSelectionScreenProps> = ({ onContinue, onBack ,Data}) => {
|
||||
const [selectedHospital, setSelectedHospital] = useState<string | null>(Data.hospital_id)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isFocus, setIsFocus] = useState(false)
|
||||
const hospitals :any=useSelector(selectHospitals)
|
||||
@ -51,7 +62,7 @@ const HospitalSelectionScreen: React.FC<HospitalSelectionScreenProps> = ({ onCon
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
onContinue(selectedHospital)
|
||||
}, 1000)
|
||||
}, 6000)
|
||||
}
|
||||
|
||||
const renderLabel = () => {
|
||||
@ -70,11 +81,7 @@ const HospitalSelectionScreen: React.FC<HospitalSelectionScreenProps> = ({ onCon
|
||||
</View> */}
|
||||
|
||||
<View style={styles.content}>
|
||||
<View style={styles.iconContainer}>
|
||||
<View style={styles.iconWrapper}>
|
||||
<Icon name="map-pin" size={32} color={Colors.primary} />
|
||||
</View>
|
||||
</View>
|
||||
<IconContainer Icon={()=><HospitalSVG/>} />
|
||||
|
||||
<Text style={styles.title}>Select Hospital</Text>
|
||||
<Text style={styles.subtitle}>Choose the hospital you're affiliated with.</Text>
|
||||
|
||||
@ -13,7 +13,7 @@ import Icon from "react-native-vector-icons/Feather"
|
||||
import { Colors, Spacing } from "../../../../shared/src/theme"
|
||||
|
||||
interface IconContainerProps {
|
||||
iconName: string
|
||||
Icon: any;
|
||||
iconSize?: number
|
||||
containerSize?: number
|
||||
backgroundColor?: string
|
||||
@ -21,7 +21,7 @@ interface IconContainerProps {
|
||||
}
|
||||
|
||||
const IconContainer: React.FC<IconContainerProps> = ({
|
||||
iconName,
|
||||
Icon,
|
||||
iconSize = 32,
|
||||
containerSize = 80,
|
||||
backgroundColor = Colors.inputBackground,
|
||||
@ -29,7 +29,7 @@ const IconContainer: React.FC<IconContainerProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View
|
||||
{/* <View
|
||||
style={[
|
||||
styles.iconWrapper,
|
||||
{
|
||||
@ -39,9 +39,9 @@ const IconContainer: React.FC<IconContainerProps> = ({
|
||||
backgroundColor,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Icon name={iconName} size={iconSize} color={iconColor} />
|
||||
</View>
|
||||
> */}
|
||||
{<Icon/>}
|
||||
{/* </View> */}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@ -16,16 +16,27 @@ import IconContainer from "./IconContainer"
|
||||
import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
|
||||
import { showError } from "../../../../shared/src/utils/helpers/Toast"
|
||||
import Icon from "react-native-vector-icons/Feather"
|
||||
import { UsernameIconSVG } 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 NameScreenProps {
|
||||
onContinue: (firstName: string, lastName: string, username: string) => void
|
||||
onBack: () => void
|
||||
Data:SignUpData
|
||||
}
|
||||
|
||||
const NameScreen: React.FC<NameScreenProps> = ({ onContinue, onBack }) => {
|
||||
const [firstName, setFirstName] = useState("")
|
||||
const [lastName, setLastName] = useState("")
|
||||
const [username, setUsername] = useState("")
|
||||
const NameScreen: React.FC<NameScreenProps> = ({ onContinue, onBack,Data }) => {
|
||||
const [firstName, setFirstName] = useState(Data.first_name)
|
||||
const [lastName, setLastName] = useState(Data.last_name)
|
||||
const [username, setUsername] = useState(Data.username)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleContinue = () => {
|
||||
@ -60,7 +71,7 @@ const NameScreen: React.FC<NameScreenProps> = ({ onContinue, onBack }) => {
|
||||
|
||||
return (
|
||||
<OnboardingContainer onBack={onBack}>
|
||||
<IconContainer iconName="user" />
|
||||
<IconContainer Icon={()=><UsernameIconSVG/>} />
|
||||
|
||||
<Text style={styles.title}>What's your name?</Text>
|
||||
<Text style={styles.subtitle}>Please enter your details to get started.</Text>
|
||||
@ -132,14 +143,14 @@ const styles = StyleSheet.create({
|
||||
marginBottom: Spacing.md,
|
||||
paddingHorizontal: Spacing.md,
|
||||
paddingVertical: 2,
|
||||
shadowColor: Colors.backButtonShadow,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 1,
|
||||
},
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
// shadowColor: Colors.backButtonShadow,
|
||||
// shadowOffset: {
|
||||
// width: 0,
|
||||
// height: 1,
|
||||
// },
|
||||
// shadowOpacity: 0.05,
|
||||
// shadowRadius: 2,
|
||||
// elevation: 1,
|
||||
},
|
||||
inputIcon: {
|
||||
marginRight: Spacing.sm,
|
||||
|
||||
@ -8,10 +8,11 @@
|
||||
*/
|
||||
|
||||
import type React from "react"
|
||||
import { View, StyleSheet, TouchableWithoutFeedback, Keyboard } from "react-native"
|
||||
import { View, StyleSheet, TouchableWithoutFeedback, Keyboard, ScrollView } from "react-native"
|
||||
import LinearGradient from "react-native-linear-gradient"
|
||||
import { Colors, Spacing } from "../../../../shared/src/theme"
|
||||
import BackButton from "./BackButton"
|
||||
import AngledGradient from "../../../../shared/src/components/Gradient/AngledGradient"
|
||||
|
||||
interface OnboardingContainerProps {
|
||||
children: React.ReactNode
|
||||
@ -21,20 +22,17 @@ interface OnboardingContainerProps {
|
||||
|
||||
const OnboardingContainer: React.FC<OnboardingContainerProps> = ({ children, onBack, showBackButton = true }) => {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<LinearGradient
|
||||
colors={Colors.backgroundGradient}
|
||||
style={styles.container}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
>
|
||||
<TouchableWithoutFeedback style={{flex:1}} onPress={Keyboard.dismiss}>
|
||||
<AngledGradient>
|
||||
{showBackButton && (
|
||||
<View style={styles.header}>
|
||||
<BackButton onPress={onBack} />
|
||||
</View>
|
||||
)}
|
||||
<ScrollView>
|
||||
<View style={styles.content}>{children}</View>
|
||||
</LinearGradient>
|
||||
</ScrollView>
|
||||
</AngledGradient>
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
}
|
||||
@ -51,7 +49,7 @@ const styles = StyleSheet.create({
|
||||
content: {
|
||||
flex: 1,
|
||||
paddingHorizontal: Spacing.lg,
|
||||
justifyContent: "center",
|
||||
justifyContent: 'center',
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -16,14 +16,25 @@ import IconContainer from "./IconContainer"
|
||||
import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
|
||||
import { showError } from "../../../../shared/src/utils/helpers/Toast"
|
||||
import Icon from "react-native-vector-icons/Feather"
|
||||
import { PasswordIconSVG } 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 PasswordScreenProps {
|
||||
onContinue: (password: string) => void
|
||||
onBack: () => void
|
||||
Data:SignUpData
|
||||
}
|
||||
|
||||
const PasswordScreen: React.FC<PasswordScreenProps> = ({ onContinue, onBack }) => {
|
||||
const [password, setPassword] = useState("")
|
||||
const PasswordScreen: React.FC<PasswordScreenProps> = ({ onContinue, onBack ,Data}) => {
|
||||
const [password, setPassword] = useState(Data.password)
|
||||
const [confirmPassword, setConfirmPassword] = useState("")
|
||||
const [isPasswordVisible, setPasswordVisible] = useState(false)
|
||||
const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false)
|
||||
@ -58,7 +69,7 @@ const PasswordScreen: React.FC<PasswordScreenProps> = ({ onContinue, onBack }) =
|
||||
|
||||
return (
|
||||
<OnboardingContainer onBack={onBack}>
|
||||
<IconContainer iconName="lock" />
|
||||
<IconContainer Icon={()=><PasswordIconSVG/>} />
|
||||
|
||||
<Text style={styles.title}>Create a password</Text>
|
||||
<Text style={styles.subtitle}>Password must be at least 8 characters</Text>
|
||||
@ -123,14 +134,14 @@ const styles = StyleSheet.create({
|
||||
marginBottom: Spacing.md,
|
||||
paddingHorizontal: Spacing.md,
|
||||
paddingVertical: 2,
|
||||
shadowColor: Colors.backButtonShadow,
|
||||
shadowOffset: {
|
||||
width: 0,
|
||||
height: 1,
|
||||
},
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 2,
|
||||
elevation: 1,
|
||||
// shadowColor: Colors.backButtonShadow,
|
||||
// shadowOffset: {
|
||||
// width: 0,
|
||||
// height: 1,
|
||||
// },
|
||||
// shadowOpacity: 0.05,
|
||||
// shadowRadius: 2,
|
||||
// elevation: 1,
|
||||
},
|
||||
inputIcon: {
|
||||
marginRight: Spacing.sm,
|
||||
@ -142,7 +153,7 @@ const styles = StyleSheet.create({
|
||||
color: Colors.textPrimary,
|
||||
},
|
||||
eyeIcon: {
|
||||
paddingLeft: Spacing.sm,
|
||||
padding: Spacing.sm,
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.xl,
|
||||
|
||||
@ -10,11 +10,13 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import { LoginScreen, SetupBiometricScreen } from '../screens';
|
||||
import { Colors } from '../../../../shared/src/theme';
|
||||
import SignUpScreen from '../screens/SignUpScreen';
|
||||
import ResetPasswordScreen from '../screens/ResetPasswordScreen';
|
||||
|
||||
export type AuthStackParamList = {
|
||||
Login: undefined;
|
||||
SetupBiometric: undefined;
|
||||
SignUpScreen:undefined;
|
||||
ResetPassword:undefined;
|
||||
};
|
||||
|
||||
const Stack = createNativeStackNavigator<AuthStackParamList>();
|
||||
@ -45,6 +47,11 @@ const AuthNavigator: React.FC = () => (
|
||||
name="SignUpScreen"
|
||||
component={SignUpScreen}
|
||||
options={{ title: 'Setup Biometric' ,headerShown:false}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ResetPassword"
|
||||
component={ResetPasswordScreen}
|
||||
options={{ title: 'Setup Biometric' ,headerShown:false}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
@ -89,6 +89,9 @@ const authSlice = createSlice({
|
||||
state.user = null;
|
||||
state.isAuthenticated = false;
|
||||
},
|
||||
updateOnboarded(state,action) {
|
||||
state.user.onboarded = action.payload;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -97,6 +100,7 @@ export const {
|
||||
loginSuccess,
|
||||
loginFailure,
|
||||
logout,
|
||||
updateOnboarded
|
||||
} = authSlice.actions;
|
||||
|
||||
export default authSlice.reducer;
|
||||
|
||||
@ -22,7 +22,7 @@ interface HospitalState {
|
||||
}
|
||||
|
||||
const initialState: HospitalState = {
|
||||
hospitals: null,
|
||||
hospitals: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
@ -13,7 +13,8 @@ import {
|
||||
TouchableWithoutFeedback,
|
||||
Keyboard,
|
||||
TouchableOpacity,
|
||||
TextInput, // Using default TextInput for container styling
|
||||
TextInput,
|
||||
ScrollView, // Using default TextInput for container styling
|
||||
} from 'react-native';
|
||||
import { Button } from '../../../../shared/src/components/Button';
|
||||
import { Colors, Spacing, Typography } from '../../../../shared/src/theme';
|
||||
@ -23,6 +24,8 @@ import { showError } from '../../../../shared/src/utils/helpers/Toast';
|
||||
import { validateEmail } from '../../../../shared/src/utils/validation/validators';
|
||||
import Icon from 'react-native-vector-icons/Feather';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import AngledGradient from '../../../../shared/src/components/Gradient/AngledGradient';
|
||||
import { LoginSVG, SecureLoginSVG } from '../../../../shared/src/components/Icons/SvgIcons';
|
||||
|
||||
const LoginScreen: React.FC = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
@ -48,12 +51,17 @@ const LoginScreen: React.FC = () => {
|
||||
// Simulate a network request for 4 seconds
|
||||
setTimeout(() => {
|
||||
setLoading(false); // Stop loading after 4 seconds
|
||||
}, 4000);
|
||||
}, 6000);
|
||||
};
|
||||
|
||||
return (
|
||||
<AngledGradient>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<ScrollView>
|
||||
<View style={styles.container}>
|
||||
<View style={{alignItems:'center'}}>
|
||||
<SecureLoginSVG/>
|
||||
</View>
|
||||
<Text style={styles.title}>Radiologist</Text>
|
||||
|
||||
{/* Email Input */}
|
||||
@ -103,14 +111,16 @@ const LoginScreen: React.FC = () => {
|
||||
/>
|
||||
|
||||
</View>
|
||||
</ScrollView>
|
||||
</TouchableWithoutFeedback>
|
||||
</AngledGradient>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
// backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
|
||||
336
app/modules/Auth/screens/ResetPasswordScreen.tsx
Normal file
336
app/modules/Auth/screens/ResetPasswordScreen.tsx
Normal file
@ -0,0 +1,336 @@
|
||||
"use client"
|
||||
|
||||
/*
|
||||
* File: ResetPasswordScreen.tsx
|
||||
* Description: Password reset screen with gradient background and shared components.
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import type React from "react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { View, Text, StyleSheet, TextInput, TouchableOpacity } from "react-native"
|
||||
import { Button } from "../../../../shared/src/components/Button"
|
||||
import OnboardingContainer from "../components/OnboardingContainer"
|
||||
import IconContainer from "../components/IconContainer"
|
||||
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 { PasswordIconSVG } from "../../../../shared/src/components/Icons/SvgIcons"
|
||||
import { authAPI } from "../services/authAPI"
|
||||
import { useDispatch, useSelector } from "react-redux"
|
||||
import { selectUser } from "../redux"
|
||||
import { updateOnboarded } from "../redux/authSlice"
|
||||
|
||||
interface PasswordScreenProps {
|
||||
onContinue: (password: string) => void
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
interface PasswordRule {
|
||||
id: string
|
||||
label: string
|
||||
validator: (password: string) => boolean
|
||||
isValid: boolean
|
||||
}
|
||||
|
||||
const ResetPasswordScreen: React.FC = () => {
|
||||
const [password, setPassword] = useState("")
|
||||
const [confirmPassword, setConfirmPassword] = useState("")
|
||||
const [isPasswordVisible, setPasswordVisible] = useState(false)
|
||||
const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false)
|
||||
const [loading, setLoading] = useState(false);
|
||||
const user=useSelector(selectUser);
|
||||
const dispatch=useDispatch()
|
||||
|
||||
const [passwordRules, setPasswordRules] = useState<PasswordRule[]>([
|
||||
{
|
||||
id: 'length',
|
||||
label: 'At least 8 characters',
|
||||
validator: (pwd: string) => pwd.length >= 8,
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
id: 'uppercase',
|
||||
label: 'One uppercase letter',
|
||||
validator: (pwd: string) => /[A-Z]/.test(pwd),
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
id: 'lowercase',
|
||||
label: 'One lowercase letter',
|
||||
validator: (pwd: string) => /[a-z]/.test(pwd),
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
id: 'number',
|
||||
label: 'One number',
|
||||
validator: (pwd: string) => /\d/.test(pwd),
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
id: 'special',
|
||||
label: 'One special character',
|
||||
validator: (pwd: string) => /[!@#$%^&*(),.?":{}|<>]/.test(pwd),
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
id: 'match',
|
||||
label: 'Passwords match',
|
||||
validator: (pwd:string) => {if(pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword){
|
||||
return true
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}, // This will be handled separately
|
||||
isValid: false
|
||||
}
|
||||
])
|
||||
console.log('password rules',passwordRules)
|
||||
const validatePassword = (pwd: string): boolean => {
|
||||
return passwordRules.every(rule => rule.isValid)
|
||||
}
|
||||
|
||||
const updatePasswordRules = (pwd: string) => {
|
||||
setPasswordRules(prevRules =>
|
||||
prevRules.map(rule => {
|
||||
if (rule.id === 'match') {
|
||||
return {
|
||||
...rule,
|
||||
isValid: pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword
|
||||
}
|
||||
}
|
||||
return {
|
||||
...rule,
|
||||
isValid: rule.validator(pwd)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const updatePasswordMatchRule = () => {
|
||||
setPasswordRules(prevRules =>
|
||||
prevRules.map(rule => {
|
||||
// if (rule.id === 'match') {
|
||||
// return {
|
||||
// ...rule,
|
||||
// isValid: password.length > 0 && confirmPassword.length > 0 && password === confirmPassword
|
||||
// }
|
||||
// }
|
||||
return rule
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updatePasswordRules(password)
|
||||
}, [password,confirmPassword])
|
||||
|
||||
// useEffect(() => {
|
||||
// updatePasswordMatchRule()
|
||||
// }, [password, confirmPassword])
|
||||
|
||||
const handlePasswordChange = (pwd: string) => {
|
||||
setPassword(pwd)
|
||||
}
|
||||
|
||||
const handleConfirmPasswordChange = (pwd: string) => {
|
||||
setConfirmPassword(pwd)
|
||||
}
|
||||
|
||||
const handleContinue = () => {
|
||||
if (!password.trim() || !confirmPassword.trim()) {
|
||||
showError("Validation Error", "Both password fields are required.")
|
||||
return
|
||||
}
|
||||
console.log('Validation',validatePassword(password))
|
||||
if (!validatePassword(password)) {
|
||||
showError("Validation Error", "Please meet all password requirements.")
|
||||
return
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showError("Validation Error", "Passwords do not match.")
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
onReset(password)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const onBack=()=>{}
|
||||
const onReset=async (password:string)=>{
|
||||
const response :any=await authAPI.changepassword({password,token:user?.access_token});
|
||||
console.log('reset response',response);
|
||||
if(response.data&&response.data.message){
|
||||
if(response.data.success){
|
||||
showSuccess(response.data.message);
|
||||
dispatch(updateOnboarded(true))
|
||||
}else{
|
||||
showError(response.data.message)
|
||||
}
|
||||
|
||||
}else{
|
||||
showError('eeror while changing password')
|
||||
}
|
||||
}
|
||||
|
||||
const renderPasswordRule = (rule: PasswordRule) => (
|
||||
<View key={rule.id} style={styles.ruleContainer}>
|
||||
<View style={[styles.checkbox, rule.isValid && styles.checkboxChecked]}>
|
||||
{rule.isValid && (
|
||||
<Icon name="check" size={12} color={"#FFFFFF"} />
|
||||
)}
|
||||
</View>
|
||||
<Text style={[styles.ruleText, rule.isValid && styles.ruleTextValid]}>
|
||||
{rule.label}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
return (
|
||||
<OnboardingContainer onBack={onBack} showBackButton={false}>
|
||||
<IconContainer Icon={()=><PasswordIconSVG/>} />
|
||||
|
||||
<Text style={styles.title}>Reset your password</Text>
|
||||
<Text style={styles.subtitle}>Create a strong password with the following requirements</Text>
|
||||
|
||||
<View style={styles.inputContainer}>
|
||||
<Icon name="lock" size={20} color={Colors.textSecondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChangeText={handlePasswordChange}
|
||||
style={styles.inputField}
|
||||
secureTextEntry={!isPasswordVisible}
|
||||
placeholderTextColor={Colors.textMuted}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => setPasswordVisible(!isPasswordVisible)} style={styles.eyeIcon}>
|
||||
<Icon name={isPasswordVisible ? "eye-off" : "eye"} size={22} color={Colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.inputContainer}>
|
||||
<Icon name="lock" size={20} color={Colors.textSecondary} style={styles.inputIcon} />
|
||||
<TextInput
|
||||
placeholder="Confirm Password"
|
||||
value={confirmPassword}
|
||||
onChangeText={handleConfirmPasswordChange}
|
||||
style={styles.inputField}
|
||||
secureTextEntry={!isConfirmPasswordVisible}
|
||||
placeholderTextColor={Colors.textMuted}
|
||||
/>
|
||||
<TouchableOpacity onPress={() => setConfirmPasswordVisible(!isConfirmPasswordVisible)} style={styles.eyeIcon}>
|
||||
<Icon name={isConfirmPasswordVisible ? "eye-off" : "eye"} size={22} color={Colors.textSecondary} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Password Rules Section */}
|
||||
<View style={styles.rulesContainer}>
|
||||
<Text style={styles.rulesTitle}>Password Requirements:</Text>
|
||||
<View style={styles.rulesGrid}>
|
||||
{passwordRules.map(renderPasswordRule)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Button title="Reset" onPress={handleContinue} style={styles.button} loading={loading} />
|
||||
</OnboardingContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
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,
|
||||
},
|
||||
inputContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: Colors.inputBackground,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.border,
|
||||
borderRadius: 12,
|
||||
marginBottom: Spacing.md,
|
||||
paddingHorizontal: Spacing.md,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
inputIcon: {
|
||||
marginRight: Spacing.sm,
|
||||
},
|
||||
inputField: {
|
||||
flex: 1,
|
||||
paddingVertical: Spacing.md,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
},
|
||||
eyeIcon: {
|
||||
paddingLeft: Spacing.sm,
|
||||
},
|
||||
rulesContainer: {
|
||||
marginTop: Spacing.sm,
|
||||
marginBottom: Spacing.lg,
|
||||
paddingHorizontal: Spacing.sm,
|
||||
},
|
||||
rulesTitle: {
|
||||
fontFamily: Typography.fontFamily.medium,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textPrimary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
rulesGrid: {
|
||||
gap: Spacing.xs,
|
||||
},
|
||||
ruleContainer: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginBottom: Spacing.xs,
|
||||
},
|
||||
checkbox: {
|
||||
width: 18,
|
||||
height: 18,
|
||||
borderRadius: 4,
|
||||
borderWidth: 2,
|
||||
borderColor: Colors.border || "#E5E5E5",
|
||||
backgroundColor: Colors.inputBackground || "#F8F9FA",
|
||||
marginRight: Spacing.sm,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
checkboxChecked: {
|
||||
backgroundColor: Colors.primary || "#007AFF",
|
||||
borderColor: Colors.primary || "#007AFF",
|
||||
},
|
||||
ruleText: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
flex: 1,
|
||||
},
|
||||
ruleTextValid: {
|
||||
color: Colors.success || Colors.primary || "#28A745",
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.xl,
|
||||
},
|
||||
})
|
||||
|
||||
export default ResetPasswordScreen
|
||||
|
||||
/*
|
||||
* End of File: ResetPasswordScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
import type React from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { View, StyleSheet, StatusBar, Platform } from "react-native"
|
||||
import { View, StyleSheet, StatusBar, Platform, Keyboard} from "react-native"
|
||||
import { Colors } from "../../../../shared/src/theme"
|
||||
import { useAppDispatch } from "../../../redux/hooks"
|
||||
import { showError, showSuccess } from "../../../../shared/src/utils/helpers/Toast"
|
||||
@ -20,16 +20,17 @@ import PasswordScreen from "../components/PasswordScreen"
|
||||
import NameScreen from "../components/NameScreen"
|
||||
import DocumentUploadScreen from "../components/DocumentUploadScreen"
|
||||
import HospitalSelectionScreen from "../components/HospitalSelectionScreen"
|
||||
import EmailAlreadyRegisteredModal from "../components/EmailAlreadyRegisteredModal"
|
||||
import { authAPI } from "../services/authAPI"
|
||||
import { setHospitals } from "../redux/hospitalSlice"
|
||||
|
||||
interface SignUpData {
|
||||
interface SignUpDataInterface {
|
||||
email: string
|
||||
password: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
username:string
|
||||
id_photo_url: {}
|
||||
id_photo_url: {}|null
|
||||
hospital_id: string
|
||||
}
|
||||
|
||||
@ -39,15 +40,46 @@ interface SignUpScreenProps {
|
||||
|
||||
const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [signUpData, setSignUpData] = useState<Partial<SignUpData>>({})
|
||||
const [showEmailRegisteredModal, setShowEmailRegisteredModal] = useState(false)
|
||||
const [signUpData, setSignUpData] = useState<Partial<SignUpDataInterface>>({
|
||||
email: '',
|
||||
password: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
username:'',
|
||||
id_photo_url: null,
|
||||
hospital_id: ''
|
||||
})
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const steps = ["email", "password", "name", "document", "hospital"]
|
||||
|
||||
const handleEmailContinue = (email: string) => {
|
||||
const handleEmailContinue = async (email: string) => {
|
||||
setSignUpData((prev) => ({ ...prev, email }))
|
||||
setCurrentStep(1)
|
||||
try {
|
||||
const response:any=await authAPI.validatemail({email});
|
||||
console.log('response',response)
|
||||
if(response.status==409&&response.data.message){
|
||||
// Show modal instead of toast for already registered email
|
||||
setShowEmailRegisteredModal(true)
|
||||
}
|
||||
if(response.status==200&&response.data.message){
|
||||
setCurrentStep(1);
|
||||
}
|
||||
} catch (error) {
|
||||
showError('error while validating your email')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setShowEmailRegisteredModal(false)
|
||||
}
|
||||
|
||||
const handleGoToLogin = () => {
|
||||
setShowEmailRegisteredModal(false)
|
||||
navigation.navigate('Login')
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
fetchHospitals();
|
||||
},[])
|
||||
@ -100,7 +132,12 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
||||
navigation.navigate('Login');
|
||||
// dispatch(setHospitals(response.data.data))
|
||||
} else {
|
||||
showError('error while signup')
|
||||
showError('error while signup');
|
||||
if( response.data && response.data.message ) {
|
||||
//@ts-ignore
|
||||
showError(response.data.message)
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log('error',error)
|
||||
@ -114,9 +151,21 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
||||
setCurrentStep(2)
|
||||
}
|
||||
|
||||
const handleNameContinue = (firstName: string, lastName: string,userName:string) => {
|
||||
const handleNameContinue = async (firstName: string, lastName: string,userName:string) => {
|
||||
setSignUpData((prev) => ({ ...prev, first_name : firstName, last_name : lastName ,username: userName}))
|
||||
setCurrentStep(3)
|
||||
try {
|
||||
const response:any=await authAPI.validateusername(userName);
|
||||
console.log('response',response)
|
||||
if(response.status==409&&response.data.message){
|
||||
showError(response.data.message);
|
||||
}
|
||||
if(response.status==200&&response.data.message){
|
||||
setCurrentStep(3);
|
||||
}
|
||||
} catch (error) {
|
||||
showError('error while validating your email')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const handleDocumentContinue = (documentUri: string) => {
|
||||
@ -152,19 +201,20 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
||||
}
|
||||
|
||||
const renderCurrentStep = () => {
|
||||
console.log('signupdate',signUpData)
|
||||
switch (currentStep) {
|
||||
case 0:
|
||||
return <EmailScreen onContinue={handleEmailContinue} onBack={handleBack} />
|
||||
return <EmailScreen onContinue={handleEmailContinue} onBack={handleBack} Data={signUpData} />
|
||||
case 1:
|
||||
return <PasswordScreen onContinue={handlePasswordContinue} onBack={handleBack} />
|
||||
return <PasswordScreen onContinue={handlePasswordContinue} onBack={handleBack} Data={signUpData} />
|
||||
case 2:
|
||||
return <NameScreen onContinue={handleNameContinue} onBack={handleBack} />
|
||||
return <NameScreen onContinue={handleNameContinue} onBack={handleBack} Data={signUpData} />
|
||||
case 3:
|
||||
return <DocumentUploadScreen onContinue={handleDocumentContinue} onBack={handleBack} />
|
||||
return <DocumentUploadScreen onContinue={handleDocumentContinue} onBack={handleBack} Data={signUpData} />
|
||||
case 4:
|
||||
return <HospitalSelectionScreen onContinue={handleHospitalContinue} onBack={handleBack} />
|
||||
return <HospitalSelectionScreen onContinue={handleHospitalContinue} onBack={handleBack} Data={signUpData} />
|
||||
default:
|
||||
return <EmailScreen onContinue={handleEmailContinue} onBack={handleBack} />
|
||||
return <EmailScreen onContinue={handleEmailContinue} onBack={handleBack} Data={signUpData}/>
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +222,12 @@ const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
||||
<View style={styles.container}>
|
||||
<StatusBar barStyle="dark-content" backgroundColor={Colors.background} />
|
||||
{renderCurrentStep()}
|
||||
|
||||
<EmailAlreadyRegisteredModal
|
||||
visible={showEmailRegisteredModal}
|
||||
onClose={handleCloseModal}
|
||||
onGoToLogin={handleGoToLogin}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@ -22,6 +22,12 @@ export const authAPI = {
|
||||
gethospitals: () => api.get('/api/hospitals/hospitals/app_user/hospitals', {},buildHeaders()),
|
||||
//user signup
|
||||
signup: (formData:any) => api.post('/api/auth/auth/admin/create-user-fromapp', formData,buildHeaders({ contentType: 'multipart/form-data' })),
|
||||
//validate email
|
||||
validatemail: (payload:{email:string}) => api.post('/api/auth/auth/check-email', payload,buildHeaders()),
|
||||
//change password
|
||||
changepassword: (payload:{password:string,token:string | undefined}) => api.post('/api/auth/onboarding/change-password', {password:payload.password},buildHeaders({token:payload.token})),
|
||||
//validate username
|
||||
validateusername: (username:string|undefined) => api.post('/api/auth/auth/check-username', {username},buildHeaders())
|
||||
// Add more endpoints as needed
|
||||
};
|
||||
|
||||
|
||||
@ -72,12 +72,12 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
|
||||
const [selectedCase, setSelectedCase] = useState<MedicalCase | null>(null);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { access_token, display_name } = useSelector((state: any) => state.auth.user);
|
||||
const user = useSelector((state: any) => state.auth.user);
|
||||
const patientData: MedicalCase[] = useSelector(selectPatients) || [];
|
||||
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
dispatch(fetchPatients(access_token));
|
||||
dispatch(fetchPatients(user?.access_token));
|
||||
}, []);
|
||||
|
||||
// Filter cases based on search
|
||||
@ -202,7 +202,7 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
|
||||
/> */}
|
||||
|
||||
<InfoCard>
|
||||
<Text style={styles.infoTitle}>Welcome, Dr. {display_name}</Text>
|
||||
<Text style={styles.infoTitle}>Welcome, Dr. {user?.display_name}</Text>
|
||||
<Text style={styles.infoText}>
|
||||
On-Call Status: <Text style={styles.active}>ACTIVE</Text>
|
||||
</Text>
|
||||
|
||||
@ -215,7 +215,7 @@ const ProfileScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
|
||||
</View>
|
||||
|
||||
{/* Onboarding Section */}
|
||||
{!user.onboarding_completed && (
|
||||
{!user.onboarded && (
|
||||
<View style={styles.onboardingSection}>
|
||||
<SectionHeader title="Onboarding" icon="alert-circle" />
|
||||
<Text style={styles.onboardingMessage}>{user.onboarding_message}</Text>
|
||||
|
||||
@ -8,11 +8,12 @@
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectIsAuthenticated } from '../modules/Auth/redux/authSelectors';
|
||||
import { selectIsAuthenticated, selectUser } from '../modules/Auth/redux/authSelectors';
|
||||
import AuthNavigator from './AuthNavigator';
|
||||
import TabNavigator from './TabNavigator';
|
||||
import { LoginScreen, SetupBiometricScreen } from '../modules/Auth/screens';
|
||||
import { DashboardScreen } from '../modules/Dashboard/screens';
|
||||
import ResetPasswordScreen from '../modules/Auth/screens/ResetPasswordScreen';
|
||||
|
||||
/**
|
||||
* AppNavigator - root navigator for the app
|
||||
@ -20,9 +21,12 @@ import { DashboardScreen } from '../modules/Dashboard/screens';
|
||||
*/
|
||||
const AppNavigator: React.FC = () => {
|
||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
||||
const user=useSelector(selectUser);
|
||||
|
||||
return (
|
||||
<NavigationContainer >
|
||||
{isAuthenticated ? <TabNavigator /> : <AuthNavigator />}
|
||||
{isAuthenticated ? user?.onboarded?<TabNavigator />: <ResetPasswordScreen/> : <AuthNavigator />}
|
||||
{/* <ResetPasswordScreen/> */}
|
||||
</NavigationContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -55,7 +55,7 @@ const styles = StyleSheet.create({
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: Colors.inactiveState,
|
||||
backgroundColor: Colors.primary,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
98
shared/src/components/Gradient/AngledGradient.tsx
Normal file
98
shared/src/components/Gradient/AngledGradient.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* File: AngledGradient.tsx
|
||||
* Description: Provides a visually appealing single color gradient background for screens.
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { StatusBar, StyleSheet, View } from 'react-native';
|
||||
import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getAdjacentColors, hexWithOpacity } from '../../../src/utils/helpers/gradient';
|
||||
import {Colors} from '../../../src/theme';
|
||||
|
||||
/**
|
||||
* AngledGradient Component
|
||||
* Renders a background with a single color linear gradient that fades from top to bottom.
|
||||
* Wraps children with this background.
|
||||
*
|
||||
* @param {ReactNode} children - The content to render on top of the gradient.
|
||||
*/
|
||||
interface AngledGradientProps {
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
const AngledGradient: React.FC<AngledGradientProps> = ({ children }) => {
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* Transparent status bar for immersive effect */}
|
||||
<StatusBar
|
||||
animated={true}
|
||||
backgroundColor={hexWithOpacity(Colors.primary,0.3)}
|
||||
barStyle="dark-content"
|
||||
/>
|
||||
|
||||
{/* Single color gradient background */}
|
||||
<Svg height="100%" width="100%" style={styles.gradientContainer}>
|
||||
<Defs>
|
||||
<LinearGradient id="mainGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<Stop offset="0%" stopColor={Colors.primary} stopOpacity="0.3" />
|
||||
<Stop offset="60%" stopColor={Colors.primary} stopOpacity="0.1" />
|
||||
<Stop offset="100%" stopColor={Colors.primary} stopOpacity="0" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
<Rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="url(#mainGradient)"
|
||||
/>
|
||||
</Svg>
|
||||
|
||||
{/* Render children on top of the gradient background */}
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
},
|
||||
gradientContainer: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
gradientOverlay: {
|
||||
position: "absolute",
|
||||
width: "150%",
|
||||
height: "150%",
|
||||
borderRadius: 400,
|
||||
opacity: 0.5,
|
||||
},
|
||||
image: {
|
||||
position: 'absolute',
|
||||
width: 400,
|
||||
height: 400,
|
||||
},
|
||||
blurContainer: {
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
export default AngledGradient;
|
||||
|
||||
/*
|
||||
* End of File: AngledGradient.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
477
shared/src/components/Icons/SvgIcons.tsx
Normal file
477
shared/src/components/Icons/SvgIcons.tsx
Normal file
@ -0,0 +1,477 @@
|
||||
import React from 'react';
|
||||
import Svg, { Path, Rect, Circle, Ellipse, Defs, LinearGradient, Stop, Polygon } from 'react-native-svg';
|
||||
import { Colors } from '../../theme';
|
||||
|
||||
|
||||
// Screen 1: Email Icon with Document/Folder
|
||||
export const EmailIconSVG = ({ width = 180, height = 180 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="folderGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FF8A65" />
|
||||
<Stop offset="100%" stopColor="#FF7043" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="documentGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FFFFFF" />
|
||||
<Stop offset="100%" stopColor="#F5F5F5" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill={Colors.primary}/>
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill={Colors.primary} />
|
||||
|
||||
{/* Orange folder/document container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="url(#folderGradient)" />
|
||||
|
||||
{/* Document pages */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="url(#documentGradient)" stroke={Colors.primary}strokeWidth="1" />
|
||||
<Rect x="42" y="42" width="36" height="26" rx="2" fill="#FFFFFF" />
|
||||
|
||||
{/* Email icon overlay */}
|
||||
<Circle cx="55" cy="45" r="8" fill="#FF5722" />
|
||||
<Path d="M50 42 L55 46 L60 42" stroke="#FFFFFF" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
|
||||
|
||||
{/* Document lines */}
|
||||
<Rect x="45" y="52" width="20" height="2" rx="1" fill={Colors.primary}/>
|
||||
<Rect x="45" y="57" width="25" height="2" rx="1" fill={Colors.primary}/>
|
||||
<Rect x="45" y="62" width="18" height="2" rx="1" fill={Colors.primary}/>
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Screen 2: Password/Security Icon with Lock
|
||||
export const PasswordIconSVG = ({ width = 180, height = 180 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="lockGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#4CAF50" />
|
||||
<Stop offset="100%" stopColor="#388E3C" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="shieldGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#81C784" />
|
||||
<Stop offset="100%" stopColor="#66BB6A" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill={Colors.primary} />
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill={Colors.primary}/>
|
||||
|
||||
{/* Green document/shield container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="url(#shieldGradient)" />
|
||||
|
||||
{/* Document base */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
|
||||
{/* Lock icon */}
|
||||
<Circle cx="60" cy="52" r="12" fill="url(#lockGradient)" />
|
||||
|
||||
{/* Lock shackle */}
|
||||
<Path
|
||||
d="M56 48 C56 45.8 57.8 44 60 44 C62.2 44 64 45.8 64 48 L64 52"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="2.5"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Lock body */}
|
||||
<Rect x="57" y="50" width="6" height="6" rx="1" fill="#FFFFFF" />
|
||||
<Circle cx="60" cy="53" r="1" fill="url(#lockGradient)" />
|
||||
|
||||
{/* Document lines */}
|
||||
<Rect x="45" y="60" width="15" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
<Rect x="65" y="60" width="10" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
<Rect x="45" y="64" width="25" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Screen 3: Username/Profile Icon
|
||||
export const UsernameIconSVG = ({ width = 180, height = 180 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="profileGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor={Colors.primary} />
|
||||
<Stop offset="100%" stopColor="#7B1FA2" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="badgeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FF9800" />
|
||||
<Stop offset="100%" stopColor="#F57C00" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill={Colors.primary}/>
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill={Colors.primary}/>
|
||||
|
||||
{/* Document container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="orange" />
|
||||
|
||||
{/* Document base */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
|
||||
{/* Profile circle background */}
|
||||
<Circle cx="60" cy="50" r="10" fill="url(#profileGradient)" />
|
||||
|
||||
{/* Profile icon - person */}
|
||||
<Circle cx="60" cy="47" r="3" fill="#FFFFFF" />
|
||||
<Path
|
||||
d="M54 56 C54 53 56.7 51 60 51 C63.3 51 66 53 66 56"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Orange badge/notification */}
|
||||
<Circle cx="68" cy="42" r="6" fill="url(#badgeGradient)" />
|
||||
<Path d="M65 42 L67 44 L71 39" stroke="#FFFFFF" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
|
||||
|
||||
{/* Document lines */}
|
||||
<Rect x="45" y="62" width="20" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
<Rect x="45" y="65" width="25" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
export const HospitalSVG = ({ width = 160, height = 160 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="buildingGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#E3F2FD" />
|
||||
<Stop offset="100%" stopColor="#BBDEFB" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="roofGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#42A5F5" />
|
||||
<Stop offset="100%" stopColor="#1E88E5" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="crossGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FF5252" />
|
||||
<Stop offset="100%" stopColor="#F44336" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="doorGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#8D6E63" />
|
||||
<Stop offset="100%" stopColor="#5D4037" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Ground/Base */}
|
||||
<Rect x="10" y="100" width="100" height="10" rx="2" fill="#A5D6A7" />
|
||||
|
||||
{/* Main Hospital Building */}
|
||||
<Rect x="25" y="45" width="70" height="55" rx="3" fill="url(#buildingGradient)" stroke="#90CAF9" strokeWidth="1" />
|
||||
|
||||
{/* Hospital Roof */}
|
||||
<Polygon points="20,45 60,25 100,45" fill="url(#roofGradient)" />
|
||||
|
||||
{/* Entrance Door */}
|
||||
<Rect x="50" y="75" width="20" height="25" rx="2" fill="url(#doorGradient)" />
|
||||
<Circle cx="65" cy="87" r="1.5" fill="#FFAB91" />
|
||||
|
||||
{/* Windows - First Floor */}
|
||||
<Rect x="32" y="55" width="12" height="12" rx="1" fill="#FFFFFF" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Rect x="76" y="55" width="12" height="12" rx="1" fill="#FFFFFF" stroke="#E1F5FE" strokeWidth="1" />
|
||||
|
||||
{/* Windows - Second Floor */}
|
||||
<Rect x="32" y="75" width="12" height="12" rx="1" fill="#FFFFFF" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Rect x="76" y="75" width="12" height="12" rx="1" fill="#FFFFFF" stroke="#E1F5FE" strokeWidth="1" />
|
||||
|
||||
{/* Window Cross Dividers */}
|
||||
<Path d="M38 55 L38 67" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M32 61 L44 61" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M82 55 L82 67" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M76 61 L88 61" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M38 75 L38 87" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M32 81 L44 81" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M82 75 L82 87" stroke="#E1F5FE" strokeWidth="1" />
|
||||
<Path d="M76 81 L88 81" stroke="#E1F5FE" strokeWidth="1" />
|
||||
|
||||
{/* Medical Cross on Building */}
|
||||
<Circle cx="60" cy="60" r="12" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
<Rect x="57" y="52" width="6" height="16" rx="1" fill="url(#crossGradient)" />
|
||||
<Rect x="52" y="57" width="16" height="6" rx="1" fill="url(#crossGradient)" />
|
||||
|
||||
{/* Hospital Sign */}
|
||||
<Rect x="45" y="35" width="30" height="8" rx="2" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
<Rect x="47" y="37" width="26" height="4" rx="1" fill="#1976D2" />
|
||||
|
||||
{/* Ambulance Area Markings */}
|
||||
<Rect x="15" y="95" width="25" height="3" rx="1" fill="#FFEB3B" />
|
||||
<Rect x="80" y="95" width="25" height="3" rx="1" fill="#FFEB3B" />
|
||||
|
||||
{/* Small Trees/Landscaping */}
|
||||
<Circle cx="15" cy="90" r="5" fill="#4CAF50" />
|
||||
<Rect x="13" y="90" width="4" height="8" fill="#8D6E63" />
|
||||
|
||||
<Circle cx="105" cy="90" r="5" fill="#4CAF50" />
|
||||
<Rect x="103" y="90" width="4" height="8" fill="#8D6E63" />
|
||||
|
||||
{/* Small Medical Cross on Roof */}
|
||||
<Rect x="58" y="28" width="2" height="6" fill="#FF5252" />
|
||||
<Rect x="56" y="30" width="6" height="2" fill="#FF5252" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Upload/Cloud Icon for Hospital Data Upload
|
||||
export const HospitalUploadSVG = ({ width = 160, height = 160 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="cloudGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#E3F2FD" />
|
||||
<Stop offset="100%" stopColor="#BBDEFB" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="hospitalIconGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FF5252" />
|
||||
<Stop offset="100%" stopColor="#F44336" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Cloud Base */}
|
||||
<Ellipse cx="60" cy="65" rx="35" ry="20" fill="url(#cloudGradient)" />
|
||||
<Circle cx="35" cy="65" r="15" fill="url(#cloudGradient)" />
|
||||
<Circle cx="85" cy="65" r="15" fill="url(#cloudGradient)" />
|
||||
<Circle cx="60" cy="50" r="18" fill="url(#cloudGradient)" />
|
||||
|
||||
{/* Upload Arrow */}
|
||||
<Path d="M60 35 L60 70" stroke="#1976D2" strokeWidth="3" strokeLinecap="round" />
|
||||
<Path d="M52 43 L60 35 L68 43" stroke="#1976D2" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" fill="none" />
|
||||
|
||||
{/* Hospital Cross in Cloud */}
|
||||
<Circle cx="60" cy="65" r="8" fill="#FFFFFF" />
|
||||
<Rect x="58" y="59" width="4" height="12" rx="1" fill="url(#hospitalIconGradient)" />
|
||||
<Rect x="55" y="62" width="10" height="4" rx="1" fill="url(#hospitalIconGradient)" />
|
||||
|
||||
{/* Data Points/Dots */}
|
||||
<Circle cx="45" cy="55" r="2" fill="#4CAF50" />
|
||||
<Circle cx="75" cy="55" r="2" fill="#4CAF50" />
|
||||
<Circle cx="50" cy="75" r="2" fill="#4CAF50" />
|
||||
<Circle cx="70" cy="75" r="2" fill="#4CAF50" />
|
||||
|
||||
{/* Upload Progress Indicator */}
|
||||
<Rect x="40" y="85" width="40" height="4" rx="2" fill="#E0E0E0" />
|
||||
<Rect x="40" y="85" width="25" height="4" rx="2" fill="#4CAF50" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
export const LoginSVG = ({ width = 120, height = 120 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="userGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#42A5F5" />
|
||||
<Stop offset="100%" stopColor="#1E88E5" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="keyGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FFB74D" />
|
||||
<Stop offset="100%" stopColor="#FF9800" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="shieldGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#66BB6A" />
|
||||
<Stop offset="100%" stopColor="#4CAF50" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill="#8E24AA" />
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill="#9C27B0" />
|
||||
|
||||
{/* Main container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="#E8EAF6" />
|
||||
|
||||
{/* Document/Screen base */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
|
||||
{/* User Profile Circle */}
|
||||
<Circle cx="55" cy="50" r="8" fill="url(#userGradient)" />
|
||||
|
||||
{/* User Icon - Head */}
|
||||
<Circle cx="55" cy="47" r="3" fill="#FFFFFF" />
|
||||
|
||||
{/* User Icon - Body */}
|
||||
<Path
|
||||
d="M49 56 C49 53 51.7 51 55 51 C58.3 51 61 53 61 56"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Key Icon */}
|
||||
<Circle cx="68" cy="52" r="6" fill="url(#keyGradient)" />
|
||||
|
||||
{/* Key shaft */}
|
||||
<Rect x="72" y="50.5" width="8" height="3" rx="1.5" fill="url(#keyGradient)" />
|
||||
|
||||
{/* Key teeth */}
|
||||
<Rect x="78" y="48.5" width="2" height="2" fill="url(#keyGradient)" />
|
||||
<Rect x="78" y="53.5" width="2" height="2" fill="url(#keyGradient)" />
|
||||
|
||||
{/* Key hole in user circle */}
|
||||
<Circle cx="55" cy="54" r="1.5" fill="#1565C0" />
|
||||
<Rect x="54.2" y="54" width="1.6" height="2" fill="#1565C0" />
|
||||
|
||||
{/* Login text representation */}
|
||||
<Rect x="45" y="60" width="15" height="2" rx="1" fill="#E0E0E0" />
|
||||
<Rect x="45" y="64" width="20" height="2" rx="1" fill="#E0E0E0" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Secure Login Icon - Lock with Shield
|
||||
export const SecureLoginSVG = ({ width = 250, height = 250 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="lockBodyGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#4CAF50" />
|
||||
<Stop offset="100%" stopColor="#388E3C" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="shackleGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#81C784" />
|
||||
<Stop offset="100%" stopColor="#66BB6A" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill={Colors.primary} />
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill={Colors.primary} />
|
||||
|
||||
{/* Main container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="#E8F5E8" />
|
||||
|
||||
{/* Document/Screen base */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
|
||||
{/* Shield Background */}
|
||||
<Path
|
||||
d="M60 42 L52 46 L52 58 C52 62 56 65 60 65 C64 65 68 62 68 58 L68 46 Z"
|
||||
fill="url(#shackleGradient)"
|
||||
/>
|
||||
|
||||
{/* Lock Body */}
|
||||
<Rect x="56" y="52" width="8" height="10" rx="2" fill="url(#lockBodyGradient)" />
|
||||
|
||||
{/* Lock Shackle */}
|
||||
<Path
|
||||
d="M58 50 C58 47.8 58.9 46 60 46 C61.1 46 62 47.8 62 50 L62 54"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Keyhole */}
|
||||
<Circle cx="60" cy="56" r="1.5" fill="#FFFFFF" />
|
||||
<Rect x="59.2" y="56" width="1.6" height="3" rx="0.8" fill="#FFFFFF" />
|
||||
|
||||
{/* Security dots/indicators */}
|
||||
<Circle cx="48" cy="48" r="1" fill="#4CAF50" />
|
||||
<Circle cx="72" cy="48" r="1" fill="#4CAF50" />
|
||||
<Circle cx="48" cy="60" r="1" fill="#4CAF50" />
|
||||
<Circle cx="72" cy="60" r="1" fill="#4CAF50" />
|
||||
|
||||
{/* Login text representation */}
|
||||
<Rect x="45" y="67" width="12" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
<Rect x="63" y="67" width="12" height="1.5" rx="0.75" fill="#E0E0E0" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Mobile Login Icon - Phone with User
|
||||
export const MobileLoginSVG = ({ width = 120, height = 120 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="phoneGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#37474F" />
|
||||
<Stop offset="100%" stopColor="#263238" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="screenGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#E3F2FD" />
|
||||
<Stop offset="100%" stopColor="#BBDEFB" />
|
||||
</LinearGradient>
|
||||
<LinearGradient id="userIconGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#2196F3" />
|
||||
<Stop offset="100%" stopColor="#1976D2" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill="#8E24AA" />
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill="#9C27B0" />
|
||||
|
||||
{/* Phone Body */}
|
||||
<Rect x="45" y="25" width="30" height="50" rx="6" fill="url(#phoneGradient)" />
|
||||
|
||||
{/* Phone Screen */}
|
||||
<Rect x="48" y="30" width="24" height="35" rx="2" fill="url(#screenGradient)" />
|
||||
|
||||
{/* User Profile on Screen */}
|
||||
<Circle cx="60" cy="42" r="6" fill="url(#userIconGradient)" />
|
||||
|
||||
{/* User Head */}
|
||||
<Circle cx="60" cy="40" r="2.5" fill="#FFFFFF" />
|
||||
|
||||
{/* User Body */}
|
||||
<Path
|
||||
d="M55.5 46 C55.5 44 57.3 42.5 60 42.5 C62.7 42.5 64.5 44 64.5 46"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{/* Login Form Elements */}
|
||||
<Rect x="50" y="50" width="20" height="2" rx="1" fill="#FFFFFF" />
|
||||
<Rect x="50" y="54" width="20" height="2" rx="1" fill="#FFFFFF" />
|
||||
|
||||
{/* Login Button */}
|
||||
<Rect x="52" y="58" width="16" height="4" rx="2" fill="#4CAF50" />
|
||||
|
||||
{/* Phone Speaker */}
|
||||
<Rect x="57" y="27" width="6" height="1.5" rx="0.75" fill="#90A4AE" />
|
||||
|
||||
{/* Home Button */}
|
||||
<Circle cx="60" cy="68" r="2" fill="#90A4AE" />
|
||||
|
||||
{/* Signal/WiFi indicators */}
|
||||
<Rect x="52" y="32" width="1" height="1" fill="#4CAF50" />
|
||||
<Rect x="54" y="32" width="1" height="1" fill="#4CAF50" />
|
||||
<Rect x="56" y="32" width="1" height="1" fill="#4CAF50" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
// Fingerprint Login Icon
|
||||
export const FingerprintLoginSVG = ({ width = 120, height = 120 }) => (
|
||||
<Svg width={width} height={height} viewBox="0 0 120 120">
|
||||
<Defs>
|
||||
<LinearGradient id="fingerprintGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<Stop offset="0%" stopColor="#FF7043" />
|
||||
<Stop offset="100%" stopColor="#D84315" />
|
||||
</LinearGradient>
|
||||
</Defs>
|
||||
|
||||
{/* Purple base/stand */}
|
||||
<Rect x="25" y="75" width="70" height="8" rx="4" fill="#8E24AA" />
|
||||
<Rect x="30" y="70" width="60" height="12" rx="6" fill="#9C27B0" />
|
||||
|
||||
{/* Main container */}
|
||||
<Rect x="35" y="35" width="50" height="40" rx="6" fill="#FFF3E0" />
|
||||
|
||||
{/* Document/Screen base */}
|
||||
<Rect x="40" y="40" width="40" height="30" rx="3" fill="#FFFFFF" stroke="#E0E0E0" strokeWidth="1" />
|
||||
|
||||
{/* Fingerprint Circles */}
|
||||
<Circle cx="60" cy="55" r="12" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1.5" />
|
||||
<Circle cx="60" cy="55" r="9" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1.2" />
|
||||
<Circle cx="60" cy="55" r="6" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1" />
|
||||
<Circle cx="60" cy="55" r="3" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="0.8" />
|
||||
|
||||
{/* Fingerprint Lines */}
|
||||
<Path d="M48 55 Q54 45 60 55" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1" />
|
||||
<Path d="M72 55 Q66 45 60 55" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1" />
|
||||
<Path d="M48 58 Q54 65 60 55" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1" />
|
||||
<Path d="M72 58 Q66 65 60 55" fill="none" stroke="url(#fingerprintGradient)" strokeWidth="1" />
|
||||
|
||||
{/* Center dot */}
|
||||
<Circle cx="60" cy="55" r="1" fill="url(#fingerprintGradient)" />
|
||||
|
||||
{/* Scan line effect */}
|
||||
<Rect x="45" y="54" width="30" height="1" fill="#4CAF50" opacity="0.7" />
|
||||
</Svg>
|
||||
);
|
||||
|
||||
89
shared/src/utils/helpers/gradient.ts
Normal file
89
shared/src/utils/helpers/gradient.ts
Normal file
@ -0,0 +1,89 @@
|
||||
function hexWithOpacity(hex:string, opacity:number) {
|
||||
hex = hex.replace('#', '');
|
||||
|
||||
// Convert hex to RGB values
|
||||
const r = parseInt(hex.slice(0, 2), 16);
|
||||
const g = parseInt(hex.slice(2, 4), 16);
|
||||
const b = parseInt(hex.slice(4, 6), 16);
|
||||
|
||||
// Blend the color with white (255, 255, 255) based on opacity
|
||||
const rAdjusted = Math.round((1 - opacity) * 255 + opacity * r);
|
||||
const gAdjusted = Math.round((1 - opacity) * 255 + opacity * g);
|
||||
const bAdjusted = Math.round((1 - opacity) * 255 + opacity * b);
|
||||
|
||||
// Convert adjusted RGB values back to hex and ensure two digits
|
||||
const rHex = rAdjusted.toString(16).padStart(2, '0');
|
||||
const gHex = gAdjusted.toString(16).padStart(2, '0');
|
||||
const bHex = bAdjusted.toString(16).padStart(2, '0');
|
||||
|
||||
// Return the new hex color with opacity simulated
|
||||
return `#${rHex}${gHex}${bHex}`;
|
||||
}
|
||||
|
||||
const hexToHSL = (hex:string) => {
|
||||
hex = hex.replace(/^#/, '');
|
||||
let r = parseInt(hex.substring(0, 2), 16) / 255;
|
||||
let g = parseInt(hex.substring(2, 4), 16) / 255;
|
||||
let b = parseInt(hex.substring(4, 6), 16) / 255;
|
||||
|
||||
let max = Math.max(r, g, b);
|
||||
let min = Math.min(r, g, b);
|
||||
let h, s, l :number = (max + min) / 2;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
let d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return { h: h * 360, s: s * 100, l: l * 100 };
|
||||
};
|
||||
|
||||
// Convert HSL to hex
|
||||
const HSLToHex = (h:number, s:number, l:number) => {
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
const k = (n:number) => (n + h / 30) % 12;
|
||||
const a = s * Math.min(l, 1 - l);
|
||||
const f = (n:number) =>
|
||||
l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
||||
return '#' +
|
||||
[255 * f(0), 255 * f(8), 255 * f(4)]
|
||||
.map(x => Math.round(x).toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
.toUpperCase();
|
||||
};
|
||||
// Convert RGB to HSL
|
||||
|
||||
|
||||
function getAdjacentColors(color:string) {
|
||||
const hsl = hexToHSL(color);
|
||||
const angle = 30;
|
||||
// get adjacent color to the primary color from color wheel
|
||||
const color1 = HSLToHex(
|
||||
(hsl.h - angle + 360) % 360,
|
||||
hsl.s,
|
||||
hsl.l
|
||||
);
|
||||
const color2 = HSLToHex(
|
||||
(hsl.h + angle) % 360,
|
||||
hsl.s,
|
||||
hsl.l
|
||||
);
|
||||
|
||||
// Adjacent color (negative direction)
|
||||
|
||||
return {
|
||||
adjacentColor1: color1,
|
||||
adjacentColor2: color2
|
||||
};
|
||||
}
|
||||
export {hexWithOpacity,getAdjacentColors}
|
||||
Loading…
Reference in New Issue
Block a user