347 lines
11 KiB
TypeScript
347 lines
11 KiB
TypeScript
import { useTheme } from '@/components/ThemeContext';
|
|
import { formatNumber, formatPhone, normalizeDigits } from '@/constants/formatPhone';
|
|
import { user_api } from '@/screens/profile/lib/api';
|
|
import { UserInfoResponseData } from '@/screens/profile/lib/type';
|
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { useRouter } from 'expo-router';
|
|
import { ArrowLeft, Edit2, Plus } from 'lucide-react-native';
|
|
import { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
ActivityIndicator,
|
|
Alert,
|
|
Image,
|
|
Pressable,
|
|
ScrollView,
|
|
StyleSheet,
|
|
Text,
|
|
TextInput,
|
|
ToastAndroid,
|
|
View,
|
|
} from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
|
export default function PersonalInfoScreen() {
|
|
const router = useRouter();
|
|
const queryClient = useQueryClient();
|
|
const { isDark } = useTheme();
|
|
const { t } = useTranslation();
|
|
|
|
const theme = {
|
|
background: isDark ? '#0f172a' : '#f8fafc',
|
|
cardBg: isDark ? '#1e293b' : '#ffffff',
|
|
text: isDark ? '#f8fafc' : '#0f172a',
|
|
textSecondary: isDark ? '#94a3b8' : '#64748b',
|
|
textTertiary: isDark ? '#64748b' : '#94a3b8',
|
|
inputBg: isDark ? '#1e293b' : '#f1f5f9',
|
|
inputBorder: isDark ? '#334155' : '#e2e8f0',
|
|
primary: '#3b82f6',
|
|
chipBg: isDark ? '#1e293b' : '#e0e7ff',
|
|
chipText: isDark ? '#f8fafc' : '#4338ca',
|
|
divider: isDark ? '#334155' : '#cbd5e1',
|
|
placeholder: isDark ? '#64748b' : '#94a3b8',
|
|
};
|
|
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [editData, setEditData] = useState<UserInfoResponseData | null>(null);
|
|
const [phone, setPhone] = useState('');
|
|
const [focused, setFocused] = useState(false);
|
|
const [showCategories, setShowCategories] = useState(false);
|
|
const [selectedCategories, setSelectedCategories] = useState<any[]>([]);
|
|
|
|
const { data: me, isLoading } = useQuery({
|
|
queryKey: ['get_me'],
|
|
queryFn: () => user_api.getMe(),
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (me?.data.data) {
|
|
setEditData(me.data);
|
|
|
|
const rawPhone = normalizeDigits(me.data.data.phone || '');
|
|
setPhone(rawPhone.startsWith('998') ? rawPhone.slice(3) : rawPhone);
|
|
|
|
setSelectedCategories(me.data.data.activate_types ?? []);
|
|
}
|
|
}, [me]);
|
|
|
|
const updateMutation = useMutation({
|
|
mutationFn: (body: any) => user_api.updateMe(body),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['get_me'] });
|
|
setIsEditing(false);
|
|
setShowCategories(false);
|
|
ToastAndroid.show(t("Ma'lumotlar yangilandi"), ToastAndroid.TOP);
|
|
},
|
|
onError: () => {
|
|
Alert.alert(t('Xatolik yuz berdi'), t('Yangilashda xatolik yuz berdi'));
|
|
},
|
|
});
|
|
|
|
const handleSave = () => {
|
|
if (!editData) return;
|
|
|
|
updateMutation.mutate({
|
|
first_name: editData.data.first_name,
|
|
phone: normalizeDigits(phone),
|
|
person_type: editData.data.person_type,
|
|
industries: editData.data.activate_types,
|
|
activate_types: editData.data.activate_types.map((e) => e.id),
|
|
|
|
company_name: editData.data.company_name,
|
|
stir: editData.data.stir,
|
|
director_full_name: editData.data.director_full_name,
|
|
address: editData.data.address,
|
|
});
|
|
};
|
|
|
|
const removeCategory = (id: number) => {
|
|
setSelectedCategories((prev) => prev.filter((c) => c.id !== id));
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
|
|
<View style={styles.topHeader}>
|
|
<Pressable onPress={() => setIsEditing(false)}>
|
|
<ArrowLeft color={theme.text} />
|
|
</Pressable>
|
|
<Text style={[styles.headerTitle, { color: theme.text }]}>{t('Tahrirlash')}</Text>
|
|
<Pressable onPress={handleSave}>
|
|
<Text style={[styles.saveButton, { color: theme.primary }]}>{t('Saqlash')}</Text>
|
|
</Pressable>
|
|
</View>
|
|
<ActivityIndicator size={'large'} />
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
/* ===================== EDIT MODE ===================== */
|
|
if (isEditing && editData) {
|
|
return (
|
|
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
|
|
<View style={styles.topHeader}>
|
|
<Pressable onPress={() => setIsEditing(false)}>
|
|
<ArrowLeft color={theme.text} />
|
|
</Pressable>
|
|
<Text style={[styles.headerTitle, { color: theme.text }]}>{t('Tahrirlash')}</Text>
|
|
<Pressable onPress={handleSave}>
|
|
{updateMutation.isPending ? (
|
|
<ActivityIndicator size={'small'} />
|
|
) : (
|
|
<Text style={[styles.saveButton, { color: theme.primary }]}>{t('Saqlash')}</Text>
|
|
)}
|
|
</Pressable>
|
|
</View>
|
|
|
|
<ScrollView style={styles.content}>
|
|
<View style={styles.editSection}>
|
|
<Text style={[styles.label, { color: theme.textSecondary }]}>{t('Ism')}</Text>
|
|
<TextInput
|
|
style={[styles.input, { backgroundColor: theme.inputBg, color: theme.text }]}
|
|
value={editData?.data.first_name}
|
|
onChangeText={(text) => setEditData((prev) => prev && { ...prev, first_name: text })}
|
|
placeholderTextColor={theme.placeholder}
|
|
/>
|
|
<Text style={[styles.label, { color: theme.textSecondary }]}>
|
|
{t('Telefon raqami')}
|
|
</Text>
|
|
<View style={[styles.phoneInputContainer, { backgroundColor: theme.inputBg }]}>
|
|
<View style={styles.prefixContainer}>
|
|
<Text
|
|
style={[styles.prefix, { color: theme.text }, focused && styles.prefixFocused]}
|
|
>
|
|
+998
|
|
</Text>
|
|
<View style={[styles.divider, { backgroundColor: theme.divider }]} />
|
|
</View>
|
|
<TextInput
|
|
style={[styles.phoneInput, { color: theme.text }]}
|
|
value={formatPhone(phone)}
|
|
onChangeText={(text) => setPhone(normalizeDigits(text))}
|
|
onFocus={() => setFocused(true)}
|
|
onBlur={() => setFocused(false)}
|
|
keyboardType="phone-pad"
|
|
placeholder="90 123 45 67"
|
|
maxLength={12}
|
|
placeholderTextColor={theme.placeholder}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
/* ===================== VIEW MODE ===================== */
|
|
return (
|
|
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
|
|
<View style={styles.topHeader}>
|
|
<Pressable onPress={() => router.push('/profile')}>
|
|
<ArrowLeft color={theme.text} />
|
|
</Pressable>
|
|
<Text style={[styles.headerTitle, { color: theme.text }]}>{t("Shaxsiy ma'lumotlar")}</Text>
|
|
<Pressable onPress={() => setIsEditing(true)}>
|
|
<Edit2 color={theme.primary} />
|
|
</Pressable>
|
|
</View>
|
|
|
|
<ScrollView style={styles.content}>
|
|
<View style={[styles.infoCard, { backgroundColor: theme.cardBg }]}>
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>{t('Ism')}</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>{me?.data.data.first_name}</Text>
|
|
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>
|
|
{t('Telefon raqami')}
|
|
</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>
|
|
{me && formatNumber(me?.data.data.phone)}
|
|
</Text>
|
|
</View>
|
|
|
|
{me?.data.data.person_type !== 'employee' && (
|
|
<View style={[styles.infoCard, { backgroundColor: theme.cardBg }]}>
|
|
<Text style={[styles.sectionTitle, { color: theme.textTertiary }]}>
|
|
{t('Kompaniya')}
|
|
</Text>
|
|
|
|
{me?.data.data.company_image && (
|
|
<Image source={{ uri: me.data.data.company_image }} style={styles.companyImage} />
|
|
)}
|
|
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>{t('Nomi')}</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>
|
|
{me?.data.data.company_name}
|
|
</Text>
|
|
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>{t('STIR')}</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>{me?.data.data.stir}</Text>
|
|
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>{t('Direktor')}</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>
|
|
{me?.data.data.director_full_name}
|
|
</Text>
|
|
{me?.data.data.address && (
|
|
<>
|
|
<Text style={[styles.infoLabel, { color: theme.textSecondary }]}>
|
|
{t('Manzil')}
|
|
</Text>
|
|
<Text style={[styles.infoValue, { color: theme.text }]}>
|
|
{me?.data.data.address}
|
|
</Text>
|
|
</>
|
|
)}
|
|
</View>
|
|
)}
|
|
<View style={styles.section}>
|
|
<View
|
|
style={{
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<Text style={[styles.sectionTitle, { color: theme.textTertiary }]}>
|
|
{t('Faoliyat sohalari')}
|
|
</Text>
|
|
<Plus
|
|
color={theme.primary}
|
|
size={24}
|
|
onPress={() => router.push('/profile/categories')}
|
|
/>
|
|
</View>
|
|
<View style={styles.fieldsContainer}>
|
|
{me?.data.data.activate_types.map((field: any) => (
|
|
<View key={field.id} style={[styles.fieldChip, { backgroundColor: theme.chipBg }]}>
|
|
<Text style={[styles.fieldText, { color: theme.chipText }]}>{field.name}</Text>
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
fieldsContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
|
|
fieldChip: {
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 10,
|
|
borderRadius: 16,
|
|
},
|
|
fieldText: { fontSize: 14, fontWeight: '600' },
|
|
section: {
|
|
padding: 10,
|
|
marginTop: 0,
|
|
},
|
|
content: {
|
|
padding: 16,
|
|
},
|
|
loadingText: {
|
|
textAlign: 'center',
|
|
marginTop: 40,
|
|
},
|
|
infoCard: {
|
|
borderRadius: 20,
|
|
padding: 16,
|
|
marginBottom: 16,
|
|
},
|
|
infoLabel: {
|
|
fontSize: 13,
|
|
marginTop: 8,
|
|
},
|
|
infoValue: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
sectionTitle: {
|
|
fontWeight: '700',
|
|
fontSize: 16,
|
|
marginBottom: 12,
|
|
},
|
|
editSection: {
|
|
gap: 12,
|
|
},
|
|
label: {
|
|
fontSize: 13,
|
|
},
|
|
input: {
|
|
borderRadius: 14,
|
|
padding: 14,
|
|
},
|
|
phoneInputContainer: {
|
|
flexDirection: 'row',
|
|
borderRadius: 14,
|
|
padding: 14,
|
|
},
|
|
phoneInput: {
|
|
flex: 1,
|
|
},
|
|
saveButton: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
companyImage: {
|
|
width: 90,
|
|
height: 90,
|
|
borderRadius: 45,
|
|
alignSelf: 'center',
|
|
marginBottom: 12,
|
|
},
|
|
topHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
padding: 16,
|
|
alignItems: 'center',
|
|
},
|
|
headerTitle: { fontSize: 18, fontWeight: '700' },
|
|
prefixContainer: { flexDirection: 'row', alignItems: 'center', marginRight: 12 },
|
|
prefix: { fontSize: 17, fontWeight: '600' },
|
|
prefixFocused: {},
|
|
divider: { width: 1, height: 24, marginLeft: 12 },
|
|
});
|