824 lines
24 KiB
TypeScript
824 lines
24 KiB
TypeScript
import AuthHeader from '@/components/ui/AuthHeader';
|
||
import { decryptToken } from '@/constants/crypto';
|
||
import { formatPhone, normalizeDigits } from '@/constants/formatPhone';
|
||
import { formatText, formatTextToLatin } from '@/constants/formatText';
|
||
import { products_api } from '@/screens/home/lib/api';
|
||
import BottomSheet, { BottomSheetBackdrop, BottomSheetFlatList, BottomSheetTextInput } from '@gorhom/bottom-sheet';
|
||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||
import { Image } from 'expo-image';
|
||
import { LinearGradient } from 'expo-linear-gradient';
|
||
import { useRouter } from 'expo-router';
|
||
import {
|
||
CheckIcon,
|
||
ChevronDown,
|
||
Globe,
|
||
Hash,
|
||
Search,
|
||
UserPlus
|
||
} from 'lucide-react-native';
|
||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||
import { useTranslation } from 'react-i18next';
|
||
import {
|
||
ActivityIndicator,
|
||
Keyboard,
|
||
StyleSheet,
|
||
Text,
|
||
TextInput,
|
||
TouchableOpacity,
|
||
View
|
||
} from 'react-native';
|
||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||
import { auth_api, GetInfo } from '../login/lib/api';
|
||
import PhonePrefix from '../login/ui/PhonePrefix';
|
||
import { UseLoginForm } from '../login/ui/UseLoginForm';
|
||
|
||
interface CoordsData {
|
||
lat: number;
|
||
lon: number;
|
||
polygon: [number, number][][];
|
||
}
|
||
|
||
export default function RegisterFormScreen() {
|
||
const router = useRouter();
|
||
const { t } = useTranslation();
|
||
const { phone, setPhone } = UseLoginForm();
|
||
const countrySheetRef = useRef<BottomSheet>(null);
|
||
const snapPoints = useMemo(() => ['60%', '90%'], []);
|
||
const [selectedCountry, setSelectedCountry] = useState<string>('UZ');
|
||
const [countrySearch, setCountrySearch] = useState<string>('');
|
||
|
||
const [stir, setStir] = useState('');
|
||
const [info, setInfo] = useState<GetInfo | null>(null);
|
||
const [loading, setLoading] = useState(false);
|
||
const [referal, setReferal] = useState('');
|
||
const [error, setError] = useState<string | null>(null)
|
||
const [district, setDistrict] = useState<string | null>(null)
|
||
const [region, setRegion] = useState<string | null>(null)
|
||
const [token, setTokens] = useState<{ name: string, value: string } | null>(null)
|
||
|
||
const [directorTinInput, setDirectorTinInput] = useState('');
|
||
|
||
const { data } = useQuery({
|
||
queryKey: ["tokens"],
|
||
queryFn: async () => auth_api.get_tokens(),
|
||
select(data) {
|
||
return data.data.data.results
|
||
},
|
||
})
|
||
|
||
useEffect(() => {
|
||
if (data?.length) {
|
||
const token = data[0]
|
||
const tokenValue = decryptToken(token.value)
|
||
if (tokenValue) {
|
||
setTokens({ name: token.key, value: tokenValue })
|
||
}
|
||
}
|
||
}, [data])
|
||
|
||
const { mutate } = useMutation({
|
||
mutationFn: (stir: string) => auth_api.get_info({ value: stir, token: token?.value || "", tokenName: token?.name || "" }),
|
||
onSuccess: (res) => {
|
||
setInfo(res.data);
|
||
setLoading(false);
|
||
setError(null)
|
||
setDistrict(res.data.address)
|
||
},
|
||
onError: () => {
|
||
setInfo(null);
|
||
setLoading(false);
|
||
setError("Foydalanuvchi topilmadi")
|
||
},
|
||
});
|
||
|
||
const { data: districts } = useQuery({
|
||
queryKey: ["discrit"],
|
||
queryFn: async () => auth_api.get_district({ token: token?.value || "", tokenName: token?.name || "" }),
|
||
enabled: !!token,
|
||
})
|
||
|
||
const { data: regions } = useQuery({
|
||
queryKey: ["regions"],
|
||
queryFn: async () => auth_api.get_region({ token: token?.value || "", tokenName: token?.name || "" }),
|
||
enabled: !!token,
|
||
})
|
||
|
||
const { data: countryResponse, isLoading: countryLoading } = useQuery({
|
||
queryKey: ['country-detail'],
|
||
queryFn: async () => products_api.getStates(),
|
||
select: (res) => res.data?.data || [],
|
||
});
|
||
|
||
const getRegionDistrictFromAddress = async (address: string) => {
|
||
try {
|
||
const encoded = encodeURIComponent(address + ", Uzbekistan");
|
||
const res = await fetch(
|
||
`https://nominatim.openstreetmap.org/search?q=${encoded}&format=json&addressdetails=1&limit=1`,
|
||
{ headers: { 'Accept-Language': 'uz', 'User-Agent': 'MyApp/1.0 (turgunboyevsamandar4@gamil.com)' } }
|
||
);
|
||
const data = await res.json();
|
||
|
||
if (data.length > 0) {
|
||
const addr = data[0].address;
|
||
return {
|
||
district: addr.county || addr.district || addr.suburb,
|
||
region: addr.state,
|
||
};
|
||
}
|
||
} catch (e) { }
|
||
return null;
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (district) {
|
||
const dis = formatText(district)?.split(" ")[0].toLocaleUpperCase()
|
||
let reg = null
|
||
if (dis) {
|
||
reg = districts?.data.find((item) => item.name.includes(dis))
|
||
};
|
||
|
||
if (reg) {
|
||
const region = regions?.data.find((item) => item.regionId == reg.regionId)
|
||
setRegion(region?.name || "")
|
||
}
|
||
}
|
||
}, [district])
|
||
|
||
|
||
const [districtId, setDistrictId] = useState<number | null>(null);
|
||
const [regionId, setRegionId] = useState<number | null>(null);
|
||
|
||
useEffect(() => {
|
||
if (!district || !countryResponse?.length) return;
|
||
|
||
const resolve = async () => {
|
||
const geo = await getRegionDistrictFromAddress(district.split(" ")[0]);
|
||
|
||
const searchRegion = geo?.region || region;
|
||
const searchDistrict = geo?.district || district
|
||
|
||
for (const country of countryResponse) {
|
||
const regionName = formatTextToLatin(searchRegion)?.split(" ")[0];
|
||
let foundRegion = null
|
||
if (regionName) {
|
||
foundRegion = country.region.find((r: any) =>
|
||
formatTextToLatin(r.name)?.includes(regionName)
|
||
);
|
||
}
|
||
|
||
if (foundRegion) {
|
||
setRegionId(foundRegion.id);
|
||
setDistrictId(null);
|
||
return;
|
||
}
|
||
|
||
for (const reg of country.region || []) {
|
||
const dis = formatTextToLatin(searchDistrict)?.split(" ")[0].toUpperCase();
|
||
let foundDistrict = null
|
||
if (dis) {
|
||
foundDistrict = reg.districts.find((d: any) => {
|
||
return formatTextToLatin(d.name)?.toUpperCase().includes(dis.slice(0, 4))
|
||
}
|
||
);
|
||
};
|
||
|
||
if (foundDistrict) {
|
||
setDistrictId(foundDistrict.id);
|
||
setRegionId(null);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
resolve();
|
||
}, [district, countryResponse]);
|
||
|
||
useEffect(() => {
|
||
if (info === null || (stir.length === 9 && info.name && info.fullName)) {
|
||
setError(null)
|
||
} else if (info?.name === null || info?.fullName === null) {
|
||
setError("Sizning shaxsiy ma'lumotlaringiz topilmadi")
|
||
} else if (!info?.selfEmployment && !info?.isItd) {
|
||
setError("Siz o'zini o'zi band qilgan yoki yakka tartibdagi tadbirkorlik bo'lishingiz kerak")
|
||
}
|
||
}, [info])
|
||
|
||
const hasDirectorTin = info?.directorPinfl && String(info.directorPinfl).length > 0;
|
||
|
||
const isDirectorTinValid = !hasDirectorTin || directorTinInput === String(info.directorPinfl);
|
||
const hasValidName = Boolean(info?.name || info?.fullName);
|
||
|
||
const filteredCountries = useMemo(() => {
|
||
if (!countrySearch.trim()) return countryResponse || [];
|
||
const q = countrySearch.toLowerCase().trim();
|
||
return (countryResponse || []).filter((c: any) => c.name?.toLowerCase().includes(q));
|
||
}, [countryResponse, countrySearch]);
|
||
|
||
const openCountrySheet = useCallback(() => {
|
||
Keyboard.dismiss();
|
||
setTimeout(() => {
|
||
countrySheetRef.current?.snapToIndex(0);
|
||
}, 100);
|
||
}, []);
|
||
|
||
const selectedCountryName = useMemo(() => {
|
||
if (!selectedCountry) return t('Tanlang');
|
||
return (
|
||
countryResponse?.find((c: any) => c.flag?.toUpperCase() === selectedCountry)?.name ||
|
||
t('Tanlang')
|
||
);
|
||
}, [selectedCountry, countryResponse, t]);
|
||
|
||
const renderBackdrop = useCallback(
|
||
(props: any) => (
|
||
<BottomSheetBackdrop
|
||
{...props}
|
||
appearsOnIndex={0}
|
||
disappearsOnIndex={-1}
|
||
opacity={0.6}
|
||
pressBehavior="close"
|
||
/>
|
||
),
|
||
[]
|
||
);
|
||
|
||
const closeCountrySheet = useCallback(() => {
|
||
countrySheetRef.current?.close();
|
||
setTimeout(() => setCountrySearch(''), 300);
|
||
}, []);
|
||
|
||
const valid =
|
||
phone.length === 9 &&
|
||
(stir.length === 9 || stir.length === 14) &&
|
||
info &&
|
||
hasValidName &&
|
||
isDirectorTinValid &&
|
||
error === null;
|
||
|
||
return (
|
||
<>
|
||
<KeyboardAwareScrollView
|
||
enableOnAndroid
|
||
enableAutomaticScroll
|
||
extraScrollHeight={120}
|
||
style={styles.keyboardScroll}
|
||
>
|
||
<View
|
||
style={styles.container}
|
||
onStartShouldSetResponder={() => {
|
||
Keyboard.dismiss();
|
||
return false;
|
||
}}
|
||
>
|
||
<LinearGradient
|
||
colors={['#0f172a', '#1e293b', '#334155']}
|
||
start={{ x: 0, y: 0 }}
|
||
end={{ x: 1, y: 1 }}
|
||
style={StyleSheet.absoluteFill}
|
||
/>
|
||
<View style={styles.decorCircle1} />
|
||
<View style={styles.decorCircle2} />
|
||
|
||
<AuthHeader />
|
||
|
||
<SafeAreaView style={{ flex: 1 }} edges={['bottom']}>
|
||
<View style={styles.scrollContent}>
|
||
<View style={styles.header}>
|
||
<View style={styles.iconContainer}>
|
||
<LinearGradient
|
||
colors={['#10b981', '#059669']}
|
||
style={styles.iconGradient}
|
||
start={{ x: 0, y: 0 }}
|
||
end={{ x: 1, y: 1 }}
|
||
>
|
||
<UserPlus size={32} color="#fff" />
|
||
</LinearGradient>
|
||
</View>
|
||
<Text style={styles.title}>{t('Ro\'yxatdan o\'tish')}</Text>
|
||
</View>
|
||
|
||
<View style={styles.card}>
|
||
<View style={styles.formGap}>
|
||
<View>
|
||
<View>
|
||
<Text style={styles.label}>{t('Davlat')}</Text>
|
||
<TouchableOpacity
|
||
style={[
|
||
styles.input,
|
||
styles.inputDisabled,
|
||
]}
|
||
onPress={openCountrySheet}
|
||
activeOpacity={0.7}
|
||
testID="country-select"
|
||
disabled
|
||
>
|
||
{countryLoading ? (
|
||
<ActivityIndicator size="small" color="#3b82f6" style={{ flex: 1 }} />
|
||
) : (
|
||
<>
|
||
<Image
|
||
source={{ uri: `https://flagcdn.com/w320/${selectedCountry.toLowerCase()}.png` }}
|
||
style={{ width: 30, height: 15 }}
|
||
resizeMode="cover"
|
||
/>
|
||
<Text
|
||
style={[
|
||
styles.textInput,
|
||
{ color: '#94a3b8' },
|
||
]}
|
||
numberOfLines={1}
|
||
>
|
||
{selectedCountryName}
|
||
</Text>
|
||
</>
|
||
)}
|
||
<ChevronDown size={18} color={'#cbd5e1'} />
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={styles.label}>{t('STIR')}</Text>
|
||
<View style={styles.input}>
|
||
<Hash size={18} color="#94a3b8" />
|
||
<TextInput
|
||
value={stir}
|
||
keyboardType="numeric"
|
||
placeholder={t('STIR')}
|
||
placeholderTextColor="#94a3b8"
|
||
style={{ flex: 1, color: "black" }}
|
||
onChangeText={(text) => {
|
||
const v = normalizeDigits(text).slice(0, 14);
|
||
setStir(v);
|
||
|
||
if (v.length === 9 || v.length === 14) {
|
||
setLoading(true);
|
||
mutate(v);
|
||
setRegionId(null)
|
||
setDistrictId(null)
|
||
setRegion(null)
|
||
setDistrict(null)
|
||
}
|
||
}}
|
||
/>
|
||
{loading && <ActivityIndicator size="small" />}
|
||
</View>
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={styles.label}>{t('Referal')}</Text>
|
||
<View style={styles.input}>
|
||
<Hash size={18} color="#94a3b8" />
|
||
<TextInput
|
||
value={referal}
|
||
placeholder={t('Referal kodi')}
|
||
placeholderTextColor="#94a3b8"
|
||
style={styles.textInput}
|
||
onChangeText={setReferal}
|
||
maxLength={9}
|
||
testID="referal-input"
|
||
/>
|
||
</View>
|
||
</View>
|
||
|
||
<View>
|
||
<Text style={styles.label}>{t('Telefon raqami')}</Text>
|
||
<View style={styles.input}>
|
||
<PhonePrefix focused={false} />
|
||
<TextInput
|
||
value={formatPhone(phone)}
|
||
placeholder="90 123 45 67"
|
||
placeholderTextColor="#94a3b8"
|
||
keyboardType="phone-pad"
|
||
style={{ flex: 1 }}
|
||
onChangeText={(t) => setPhone(normalizeDigits(t))}
|
||
/>
|
||
</View>
|
||
</View>
|
||
|
||
{hasDirectorTin && (
|
||
<View>
|
||
<Text style={styles.label}>{t('Direktor STIR')}</Text>
|
||
<View style={[styles.input, { backgroundColor: isDirectorTinValid ? '#f0fdf4' : '#f8fafc' }]}>
|
||
<Hash size={18} color="#94a3b8" />
|
||
<TextInput
|
||
value={directorTinInput}
|
||
keyboardType="numeric"
|
||
placeholder={t('Direktor STIR')}
|
||
placeholderTextColor="#94a3b8"
|
||
style={{ flex: 1 }}
|
||
maxLength={14}
|
||
onChangeText={(t) => setDirectorTinInput(normalizeDigits(t))}
|
||
/>
|
||
</View>
|
||
|
||
{directorTinInput.length === 14 && !isDirectorTinValid && (
|
||
<Text style={styles.error}>{t('Direktor STIR noto‘g‘ri')}</Text>
|
||
)}
|
||
</View>
|
||
)}
|
||
|
||
{error !== null ?
|
||
<Text style={styles.notFound}>{t(error)}</Text>
|
||
: info && hasValidName &&
|
||
<Text style={styles.info}>{info.fullName || info.name}</Text>
|
||
}
|
||
|
||
<TouchableOpacity
|
||
disabled={!valid}
|
||
style={[styles.btn, !valid && styles.disabled]}
|
||
onPress={() => {
|
||
if (error === null) {
|
||
router.push({
|
||
pathname: '/(auth)/select-category',
|
||
params: {
|
||
phone,
|
||
first_name: stir.length === 9 ? info?.director : info?.fullName,
|
||
last_name: stir.length === 9 ? info?.director : info?.fullName,
|
||
company_name: info?.name,
|
||
district: districtId !== null ? districtId : regionId,
|
||
address: districtId !== null ? districtId : regionId,
|
||
director_full_name: stir.length === 9 ? info?.director : info?.fullName,
|
||
stir,
|
||
referal,
|
||
person_type: stir.length === 9 ? 'legal_entity' : info?.selfEmployment ? 'band' : info?.isItd ? 'ytt' : 'ytt',
|
||
},
|
||
})
|
||
}
|
||
}}
|
||
>
|
||
<Text style={styles.btnText}>{t('Davom etish')}</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</SafeAreaView>
|
||
</View >
|
||
</KeyboardAwareScrollView >
|
||
|
||
<BottomSheet
|
||
ref={countrySheetRef}
|
||
index={-1}
|
||
snapPoints={snapPoints}
|
||
enablePanDownToClose={true}
|
||
enableDynamicSizing={false}
|
||
enableOverDrag={false}
|
||
backdropComponent={renderBackdrop}
|
||
backgroundStyle={styles.bottomSheetBg}
|
||
handleIndicatorStyle={styles.handleIndicator}
|
||
android_keyboardInputMode="adjustResize"
|
||
keyboardBehavior="interactive"
|
||
keyboardBlurBehavior="restore"
|
||
>
|
||
<View style={styles.sheetHeader}>
|
||
<Text style={styles.sheetTitle}>{t('Davlat')}</Text>
|
||
</View>
|
||
|
||
<View style={styles.searchContainer}>
|
||
<Search size={16} color="#94a3b8" />
|
||
<BottomSheetTextInput
|
||
value={countrySearch}
|
||
onChangeText={setCountrySearch}
|
||
placeholder={t('Qidirish...')}
|
||
placeholderTextColor="#94a3b8"
|
||
style={styles.searchInput}
|
||
clearButtonMode="while-editing"
|
||
autoCorrect={false}
|
||
/>
|
||
</View>
|
||
|
||
<BottomSheetFlatList
|
||
data={filteredCountries}
|
||
keyExtractor={(item: any) => item.id?.toString()}
|
||
contentContainerStyle={styles.listContainer}
|
||
showsVerticalScrollIndicator={false}
|
||
keyboardShouldPersistTaps="handled"
|
||
ListEmptyComponent={
|
||
<View style={styles.emptyList}>
|
||
<Text style={styles.emptyListText}>{t('Natija topilmadi')}</Text>
|
||
</View>
|
||
}
|
||
renderItem={({ item }: { item: any }) => {
|
||
const isSelected = item.flag?.toUpperCase() === selectedCountry;
|
||
const flagCode = item.flag ? item.flag.toLowerCase() : '';
|
||
return (
|
||
<TouchableOpacity
|
||
style={[styles.listItem, isSelected && styles.selectedListItem]}
|
||
onPress={() => {
|
||
setSelectedCountry(item.flag?.toUpperCase());
|
||
closeCountrySheet();
|
||
}}
|
||
activeOpacity={0.7}
|
||
>
|
||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10, flex: 1 }}>
|
||
{flagCode ? (
|
||
<Image
|
||
source={{ uri: `https://flagcdn.com/w320/${flagCode}.png` }}
|
||
style={{ width: 34, height: 22, borderRadius: 2 }}
|
||
/>
|
||
) : (
|
||
<Globe size={20} color={isSelected ? '#2563eb' : '#94a3b8'} />
|
||
)}
|
||
<Text style={[styles.listItemText, isSelected && styles.selectedListItemText]}>
|
||
{item.name}
|
||
</Text>
|
||
</View>
|
||
{isSelected && (
|
||
<View style={styles.checkmark}>
|
||
<CheckIcon color="#3b82f6" strokeWidth={2.5} size={16} />
|
||
</View>
|
||
)}
|
||
</TouchableOpacity>
|
||
);
|
||
}}
|
||
/>
|
||
</BottomSheet >
|
||
</>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
keyboardScroll: {
|
||
flex: 1,
|
||
backgroundColor: '#0f172a',
|
||
},
|
||
container: {
|
||
flex: 1,
|
||
backgroundColor: '#0f172a',
|
||
minHeight: '100%',
|
||
},
|
||
scrollContent: {
|
||
flexGrow: 1,
|
||
paddingHorizontal: 24,
|
||
paddingBottom: 40,
|
||
paddingTop: 10,
|
||
},
|
||
header: {
|
||
alignItems: 'center',
|
||
marginBottom: 28,
|
||
},
|
||
iconContainer: {
|
||
marginBottom: 16,
|
||
},
|
||
iconGradient: {
|
||
width: 72,
|
||
height: 72,
|
||
borderRadius: 22,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
shadowColor: '#10b981',
|
||
shadowOffset: { width: 0, height: 8 },
|
||
shadowOpacity: 0.3,
|
||
shadowRadius: 12,
|
||
elevation: 8,
|
||
},
|
||
title: {
|
||
fontSize: 26,
|
||
fontWeight: '800' as const,
|
||
color: '#ffffff',
|
||
marginBottom: 8,
|
||
letterSpacing: 0.5,
|
||
},
|
||
subtitle: {
|
||
fontSize: 14,
|
||
color: '#94a3b8',
|
||
textAlign: 'center',
|
||
lineHeight: 20,
|
||
paddingHorizontal: 10,
|
||
},
|
||
card: {
|
||
backgroundColor: '#ffffff',
|
||
borderRadius: 28,
|
||
padding: 24,
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 10 },
|
||
shadowOpacity: 0.3,
|
||
shadowRadius: 20,
|
||
elevation: 10,
|
||
},
|
||
formGap: {
|
||
gap: 16,
|
||
},
|
||
label: {
|
||
fontWeight: '700' as const,
|
||
color: '#475569',
|
||
marginBottom: 6,
|
||
fontSize: 14,
|
||
},
|
||
input: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
backgroundColor: '#f8fafc',
|
||
borderRadius: 14,
|
||
paddingHorizontal: 12,
|
||
height: 52,
|
||
borderWidth: 1,
|
||
borderColor: '#e2e8f0',
|
||
gap: 8,
|
||
},
|
||
bottomSheetBg: {
|
||
backgroundColor: '#ffffff',
|
||
borderTopLeftRadius: 24,
|
||
borderTopRightRadius: 24,
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: -2 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 8,
|
||
elevation: 5,
|
||
},
|
||
handleIndicator: {
|
||
width: 40,
|
||
height: 4,
|
||
borderRadius: 2,
|
||
backgroundColor: '#cbd5e1',
|
||
},
|
||
sheetHeader: {
|
||
paddingHorizontal: 20,
|
||
paddingVertical: 16,
|
||
borderBottomWidth: 1,
|
||
borderBottomColor: '#e2e8f0',
|
||
},
|
||
sheetTitle: {
|
||
fontSize: 18,
|
||
fontWeight: '700',
|
||
color: '#0f172a',
|
||
},
|
||
listContainer: {
|
||
padding: 16,
|
||
paddingBottom: 40,
|
||
},
|
||
listItem: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
padding: 14,
|
||
borderRadius: 12,
|
||
marginBottom: 8,
|
||
borderWidth: 1,
|
||
backgroundColor: '#ffffff',
|
||
borderColor: '#e2e8f0',
|
||
},
|
||
selectedListItem: {
|
||
backgroundColor: '#eff6ff',
|
||
borderColor: '#3b82f6',
|
||
},
|
||
listItemText: {
|
||
fontSize: 15,
|
||
fontWeight: '500',
|
||
color: '#1e293b',
|
||
},
|
||
selectedListItemText: {
|
||
color: '#2563eb',
|
||
fontWeight: '600',
|
||
},
|
||
checkmark: {
|
||
width: 24,
|
||
height: 24,
|
||
borderRadius: 12,
|
||
backgroundColor: '#dbeafe',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
},
|
||
searchContainer: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
backgroundColor: '#f1f5f9',
|
||
borderRadius: 12,
|
||
marginHorizontal: 16,
|
||
marginVertical: 12,
|
||
paddingHorizontal: 12,
|
||
gap: 8,
|
||
height: 44,
|
||
borderWidth: 1,
|
||
borderColor: '#e2e8f0',
|
||
},
|
||
searchInput: {
|
||
flex: 1,
|
||
fontSize: 15,
|
||
color: '#1e293b',
|
||
paddingVertical: 0,
|
||
},
|
||
emptyList: {
|
||
alignItems: 'center',
|
||
paddingVertical: 32,
|
||
},
|
||
emptyListText: {
|
||
fontSize: 14,
|
||
color: '#94a3b8',
|
||
fontWeight: '500',
|
||
},
|
||
textInput: {
|
||
flex: 1,
|
||
fontSize: 15,
|
||
color: '#1e293b',
|
||
},
|
||
passportRow: {
|
||
flexDirection: 'row',
|
||
gap: 10,
|
||
},
|
||
passportSeries: {
|
||
width: 100,
|
||
flex: undefined,
|
||
},
|
||
passportNumber: {
|
||
flex: 1,
|
||
},
|
||
infoBox: {
|
||
backgroundColor: '#f0fdf4',
|
||
padding: 14,
|
||
marginTop: 10,
|
||
borderRadius: 14,
|
||
borderWidth: 1,
|
||
borderColor: '#bbf7d0',
|
||
},
|
||
infoLabel: {
|
||
fontSize: 11,
|
||
fontWeight: '600' as const,
|
||
color: '#059669',
|
||
marginBottom: 2,
|
||
textTransform: 'uppercase',
|
||
letterSpacing: 0.5,
|
||
},
|
||
infoText: {
|
||
fontWeight: '700' as const,
|
||
color: '#166534',
|
||
fontSize: 14,
|
||
},
|
||
errorBox: {
|
||
backgroundColor: '#fef2f2',
|
||
padding: 14,
|
||
borderRadius: 14,
|
||
borderWidth: 1,
|
||
borderColor: '#fecaca',
|
||
},
|
||
errorText: {
|
||
fontWeight: '700' as const,
|
||
color: '#dc2626',
|
||
fontSize: 14,
|
||
},
|
||
btn: {
|
||
height: 54,
|
||
backgroundColor: '#2563eb',
|
||
borderRadius: 16,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
shadowColor: '#2563eb',
|
||
shadowOffset: { width: 0, height: 4 },
|
||
shadowOpacity: 0.3,
|
||
shadowRadius: 8,
|
||
elevation: 6,
|
||
},
|
||
disabled: {
|
||
opacity: 0.45,
|
||
},
|
||
btnText: {
|
||
color: '#fff',
|
||
fontWeight: '800' as const,
|
||
fontSize: 16,
|
||
},
|
||
decorCircle1: {
|
||
position: 'absolute',
|
||
top: -150,
|
||
right: -100,
|
||
width: 400,
|
||
height: 400,
|
||
borderRadius: 200,
|
||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||
},
|
||
decorCircle2: {
|
||
position: 'absolute',
|
||
bottom: -100,
|
||
left: -150,
|
||
width: 350,
|
||
height: 350,
|
||
borderRadius: 175,
|
||
backgroundColor: 'rgba(16, 185, 129, 0.08)',
|
||
},
|
||
inputDisabled: {
|
||
backgroundColor: '#f1f5f9',
|
||
borderColor: '#e2e8f0',
|
||
},
|
||
|
||
info: {
|
||
padding: 12,
|
||
borderRadius: 12,
|
||
fontWeight: '700',
|
||
backgroundColor: '#f0fdf4',
|
||
},
|
||
error: {
|
||
color: '#dc2626',
|
||
fontSize: 12,
|
||
marginTop: 4,
|
||
fontWeight: '600',
|
||
},
|
||
notFound: {
|
||
backgroundColor: '#fef2f2',
|
||
padding: 12,
|
||
borderRadius: 12,
|
||
fontWeight: '700',
|
||
color: '#dc2626',
|
||
borderWidth: 1,
|
||
borderColor: '#fecaca',
|
||
},
|
||
}); |