update register page ui and api

This commit is contained in:
Samandar Turgunboyev
2026-02-24 11:28:06 +05:00
parent 0c9e0811ea
commit 8edd45d1ad
18 changed files with 14795 additions and 313 deletions

View File

@@ -1,6 +1,14 @@
// app/(auth)/_layout.tsx
import { RegisterProvider } from '@/screens/auth/register/lib/useRegisterStore';
import { Slot } from 'expo-router';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function AuthLayout() {
return <Slot />;
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<RegisterProvider>
<Slot />
</RegisterProvider>
</GestureHandlerRootView>
)
}

View File

@@ -0,0 +1,15 @@
import RegisterFormScreen from '@/screens/auth/register/RegisterForm';
import React from 'react';
import { StyleSheet, View } from 'react-native';
export default function Index() {
return (
<View style={styles.safeArea}>
<RegisterFormScreen />
</View>
);
}
const styles = StyleSheet.create({
safeArea: { flex: 1, backgroundColor: '#0f172a' },
});

View File

@@ -1,21 +1,247 @@
import RegisterScreen from '@/screens/auth/register/RegisterScreen';
import AuthHeader from '@/components/ui/AuthHeader';
import { PersonType, useRegister } from '@/screens/auth/register/lib/useRegisterStore';
import { LinearGradient } from 'expo-linear-gradient';
import { useRouter } from 'expo-router';
import { Building2, ChevronRight, ShieldCheck, User } from 'lucide-react-native';
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import { useTranslation } from 'react-i18next';
import {
Animated,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
const PERSON_TYPES: {
key: PersonType;
label: string;
description: string;
icon: React.ReactNode;
gradient: [string, string];
}[] = [
{
key: 'legal_entity',
label: 'Yuridik shaxs',
description: "Tashkilot yoki korxona",
icon: <Building2 size={28} color="#fff" />,
gradient: ['#f59e0b', '#d97706'],
},
{
key: 'yatt',
label: 'YATT',
description: "Yakka tartibdagi tadbirkor",
icon: <User size={28} color="#fff" />,
gradient: ['#10b981', '#059669'],
},
{
key: 'band',
label: "O'zini o'zi band qilgan",
description: "O'z faoliyatini mustaqil yurituvchi",
icon: <ShieldCheck size={28} color="#fff" />,
gradient: ['#3b82f6', '#2563eb'],
},
];
function TypeCard({
item,
index,
onPress,
}: {
item: (typeof PERSON_TYPES)[number];
index: number;
onPress: () => void;
}) {
const scale = React.useRef(new Animated.Value(1)).current;
const { t } = useTranslation()
const handlePressIn = () => {
Animated.spring(scale, {
toValue: 0.96,
useNativeDriver: true,
}).start();
};
const handlePressOut = () => {
Animated.spring(scale, {
toValue: 1,
friction: 4,
useNativeDriver: true,
}).start();
};
export default function Index() {
return (
<View style={styles.safeArea}>
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
<Animated.View style={{ transform: [{ scale }], marginBottom: 14 }}>
<TouchableOpacity
style={styles.card}
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
activeOpacity={1}
testID={`person-type-${item.key}`}
>
<RegisterScreen />
</ScrollView>
<LinearGradient
colors={item.gradient}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.cardIcon}
>
{item.icon}
</LinearGradient>
<View style={styles.cardContent}>
<Text style={styles.cardTitle}>{t(item.label)}</Text>
<Text style={styles.cardDescription}>{t(item.description)}</Text>
</View>
<ChevronRight size={20} color="#94a3b8" />
</TouchableOpacity>
</Animated.View>
);
}
export default function PersonTypeScreen() {
const router = useRouter();
const { t } = useTranslation();
const { setPersonType, reset } = useRegister();
const handleSelect = (type: PersonType) => {
setPersonType(type);
router.push('/(auth)/register-form');
};
return (
<View style={styles.container}>
<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={styles.safeArea}>
<View style={styles.header}>
<Text style={styles.title}>{t("Ro'yxatdan o'tish")}</Text>
<Text style={styles.subtitle}>
{t("Faoliyat turingizni tanlang")}
</Text>
</View>
<ScrollView style={styles.cardList} showsVerticalScrollIndicator={false}>
{PERSON_TYPES.map((item, index) => (
<TypeCard
key={item.key}
item={item}
index={index}
onPress={() => { handleSelect(item.key); reset() }}
/>
))}
</ScrollView>
<View style={styles.footer}>
<TouchableOpacity onPress={() => router.back()} testID="go-to-login">
<Text style={styles.footerText}>
{t("Hisobingiz bormi?")} <Text style={styles.footerLink}>{t("Kirish")}</Text>
</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
</View>
);
}
const styles = StyleSheet.create({
safeArea: { flex: 1, backgroundColor: '#0f172a' },
container: {
flex: 1,
backgroundColor: '#0f172a',
},
safeArea: {
flex: 1,
paddingHorizontal: 14,
},
header: {
alignItems: 'center',
marginBottom: 20,
marginTop: 20,
},
title: {
fontSize: 28,
fontWeight: '800' as const,
color: '#ffffff',
marginBottom: 10,
letterSpacing: 0.5,
},
subtitle: {
fontSize: 15,
color: '#94a3b8',
textAlign: 'center',
lineHeight: 22,
},
cardList: {
marginBottom: 14,
},
card: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(255, 255, 255, 0.06)',
borderRadius: 20,
padding: 18,
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 0.1)',
gap: 16,
},
cardIcon: {
width: 56,
height: 56,
borderRadius: 16,
alignItems: 'center',
justifyContent: 'center',
},
cardContent: {
flex: 1,
},
cardTitle: {
fontSize: 17,
fontWeight: '700' as const,
color: '#ffffff',
marginBottom: 4,
},
cardDescription: {
fontSize: 13,
color: '#94a3b8',
lineHeight: 18,
},
footer: {
marginTop: 'auto' as const,
paddingBottom: 20,
alignItems: 'center',
},
footerText: {
color: '#94a3b8',
fontSize: 14,
},
footerLink: {
color: '#3b82f6',
fontWeight: '700' as const,
},
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)',
},
});

View File

@@ -28,10 +28,15 @@ interface Category {
export default function CategorySelectScreen() {
const router = useRouter();
const { t } = useTranslation();
const { phone, stir, person_type } = useLocalSearchParams<{
const { phone, stir, person_type, director_full_name, referal, first_name, last_name, middle_name } = useLocalSearchParams<{
phone: string;
stir: string;
person_type: 'band' | 'ytt';
referal: string;
director_full_name: string;
first_name: string;
last_name: string;
middle_name: string;
}>();
const [selected, setSelected] = useState<number | null>(null);
@@ -63,6 +68,10 @@ export default function CategorySelectScreen() {
stir: string;
person_type: string;
activate_types: number[];
director_full_name: string;
referal: string;
first_name: string;
last_name: string;
}) => auth_api.register(body),
onSuccess: async () => {
router.replace('/(auth)/register-confirm');
@@ -94,6 +103,8 @@ export default function CategorySelectScreen() {
setSelected(null);
};
const full_name = first_name.length > 0 ? first_name + ' ' + last_name + ' ' + middle_name : director_full_name;
return (
<View style={styles.safeArea}>
<AuthHeader />
@@ -138,6 +149,10 @@ export default function CategorySelectScreen() {
person_type,
phone: `998${phone}`,
stir,
referal: referal,
director_full_name: director_full_name,
first_name: full_name,
last_name: last_name,
});
}}
>