fitst commit
This commit is contained in:
460
screens/profile/ui/PersonalInfoTab.tsx
Normal file
460
screens/profile/ui/PersonalInfoTab.tsx
Normal file
@@ -0,0 +1,460 @@
|
||||
import { formatPhone, normalizeDigits } from '@/constants/formatPhone';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { Edit2, Plus } from 'lucide-react-native';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Alert, Modal, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
||||
import { user_api } from '../lib/api';
|
||||
import { useProfileData } from '../lib/ProfileDataContext';
|
||||
import { UserInfoResponseData } from '../lib/type';
|
||||
|
||||
export function PersonalInfoTab() {
|
||||
const { personalInfo, updatePersonalInfo } = useProfileData();
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [addFieldModalVisible, setAddFieldModalVisible] = useState(false);
|
||||
const [newField, setNewField] = useState('');
|
||||
const [focused, setFocused] = useState(false);
|
||||
const [editData, setEditData] = useState<UserInfoResponseData | undefined>(undefined);
|
||||
const [phone, setPhone] = useState('');
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// GET ME
|
||||
const {
|
||||
data: me,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ['get_me'],
|
||||
queryFn: () => user_api.getMe(),
|
||||
select: (res) => {
|
||||
setEditData(res.data.data);
|
||||
setPhone(res.data.data.phone || '');
|
||||
return res;
|
||||
},
|
||||
});
|
||||
|
||||
// UPDATE ME mutation
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: (body: {
|
||||
first_name: string;
|
||||
industries: {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
external_id: null | number;
|
||||
level: number;
|
||||
is_leaf: boolean;
|
||||
icon_name: null | string;
|
||||
parent: {
|
||||
id: number;
|
||||
name: string;
|
||||
code: string;
|
||||
};
|
||||
}[];
|
||||
phone: string;
|
||||
person_type: 'employee' | 'legal_entity' | 'ytt' | 'band';
|
||||
}) => user_api.updateMe(body),
|
||||
onSuccess: (res) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['get_me'] });
|
||||
setEditModalVisible(false);
|
||||
Alert.alert('Muvaffaqiyat', 'Ma’lumotlar yangilandi');
|
||||
},
|
||||
onError: () => {
|
||||
Alert.alert('Xatolik', 'Ma’lumotlarni yangilashda xatolik yuz berdi');
|
||||
},
|
||||
});
|
||||
|
||||
// const handleSave = () => {
|
||||
// if (!editData) return;
|
||||
|
||||
// // Backendga yuboriladigan data
|
||||
// const payload: {
|
||||
// first_name: string;
|
||||
// industries: {
|
||||
// id: number;
|
||||
// name: string;
|
||||
// code: string;
|
||||
// external_id: null | number;
|
||||
// level: number;
|
||||
// is_leaf: boolean;
|
||||
// icon_name: null | string;
|
||||
// parent: {
|
||||
// id: number;
|
||||
// name: string;
|
||||
// code: string;
|
||||
// };
|
||||
// }[];
|
||||
// person_type: 'employee' | 'legal_entity' | 'ytt' | 'band';
|
||||
// phone: string;
|
||||
// } = {
|
||||
// first_name: editData.director_full_name,
|
||||
// phone: normalizeDigits(phone),
|
||||
// industries:
|
||||
// };
|
||||
|
||||
// updateMutation.mutate();
|
||||
// };
|
||||
|
||||
const handlePhone = (text: string) => {
|
||||
const n = normalizeDigits(text);
|
||||
setPhone(n);
|
||||
};
|
||||
|
||||
const handleAddField = () => {
|
||||
if (newField.trim()) {
|
||||
updatePersonalInfo({
|
||||
activityFields: [...personalInfo.activityFields, newField.trim()],
|
||||
});
|
||||
setNewField('');
|
||||
setAddFieldModalVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveField = (field: string) => {
|
||||
Alert.alert('Soha olib tashlash', `"${field}" sohasini olib tashlashni xohlaysizmi?`, [
|
||||
{ text: 'Bekor qilish', style: 'cancel' },
|
||||
{
|
||||
text: 'Olib tashlash',
|
||||
style: 'destructive',
|
||||
onPress: () => {
|
||||
updatePersonalInfo({
|
||||
activityFields: personalInfo.activityFields.filter((f) => f !== field),
|
||||
});
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (me?.data.data) {
|
||||
setEditData(me.data.data);
|
||||
setPhone(me.data.data.phone || '');
|
||||
}
|
||||
}, [me]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* Personal Info Card */}
|
||||
<View style={styles.card}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Text style={styles.cardTitle}>Shaxsiy ma'lumotlar</Text>
|
||||
<Pressable onPress={() => setEditModalVisible(true)} style={styles.editButton}>
|
||||
<Edit2 size={18} color="#3b82f6" />
|
||||
<Text style={styles.editButtonText}>Tahrirlash</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoRow}>
|
||||
<Text style={styles.label}>Ism</Text>
|
||||
<Text style={styles.value}>{me?.data.data.director_full_name}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoRow}>
|
||||
<Text style={styles.label}>Telefon raqam</Text>
|
||||
<Text style={styles.value}>+{me?.data.data.phone}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.infoRow}>
|
||||
<Text style={styles.label}>Foydalanuvchi turi</Text>
|
||||
<Text style={styles.value}>
|
||||
{me?.data.data.person_type === 'employee'
|
||||
? 'Xodim'
|
||||
: me?.data.data.person_type === 'legal_entity'
|
||||
? 'Yuridik shaxs'
|
||||
: me?.data.data.person_type === 'ytt'
|
||||
? 'Yakka tartibdagi tadbirkor'
|
||||
: "O'zini o'zi band qilgan shaxs"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Activity Fields Card */}
|
||||
<View style={styles.card}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Text style={styles.cardTitle}>Faoliyat sohalari</Text>
|
||||
<Pressable onPress={() => setAddFieldModalVisible(true)} style={styles.addButton}>
|
||||
<Plus size={18} color="#10b981" />
|
||||
<Text style={styles.addButtonText}>Qo'shish</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<View style={styles.fieldsContainer}>
|
||||
{me?.data.data.activate_types.map((field) => (
|
||||
<View key={field.id} style={styles.fieldChip}>
|
||||
<Text style={styles.fieldText}>{field.name}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Edit Modal */}
|
||||
<Modal
|
||||
visible={editModalVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => setEditModalVisible(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<Text style={styles.modalTitle}>Ma'lumotlarni tahrirlash</Text>
|
||||
|
||||
<Text style={styles.inputLabel}>Ism</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={editData?.director_full_name}
|
||||
onChangeText={(text) =>
|
||||
setEditData((prev) => prev && { ...prev, director_full_name: text })
|
||||
}
|
||||
placeholderTextColor="#64748b"
|
||||
/>
|
||||
|
||||
<Text style={styles.label}>Telefon</Text>
|
||||
<View style={styles.inputBox}>
|
||||
<View style={styles.prefixContainer}>
|
||||
<Text style={[styles.prefix, focused && styles.prefixFocused]}>+998</Text>
|
||||
<View style={styles.divider} />
|
||||
</View>
|
||||
<TextInput
|
||||
style={{ ...styles.input, borderWidth: 0 }}
|
||||
value={formatPhone(phone)}
|
||||
onChangeText={handlePhone}
|
||||
onFocus={() => setFocused(true)}
|
||||
onBlur={() => setFocused(false)}
|
||||
keyboardType="phone-pad"
|
||||
placeholder="90 123 45 67"
|
||||
maxLength={12}
|
||||
placeholderTextColor="#94a3b8"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<Pressable
|
||||
onPress={() => setEditModalVisible(false)}
|
||||
style={[styles.modalButton, styles.cancelButton]}
|
||||
>
|
||||
<Text style={styles.cancelButtonText}>Bekor qilish</Text>
|
||||
</Pressable>
|
||||
<Pressable style={[styles.modalButton, styles.saveButton]}>
|
||||
<Text style={styles.saveButtonText}>Saqlash</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
{/* Add Field Modal */}
|
||||
<Modal
|
||||
visible={addFieldModalVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => setAddFieldModalVisible(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<Text style={styles.modalTitle}>Faoliyat sohasini qo'shish</Text>
|
||||
|
||||
<Text style={styles.inputLabel}>Soha nomi</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={newField}
|
||||
onChangeText={setNewField}
|
||||
placeholder="Masalan: IT"
|
||||
placeholderTextColor="#64748b"
|
||||
/>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
setNewField('');
|
||||
setAddFieldModalVisible(false);
|
||||
}}
|
||||
style={[styles.modalButton, styles.cancelButton]}
|
||||
>
|
||||
<Text style={styles.cancelButtonText}>Bekor qilish</Text>
|
||||
</Pressable>
|
||||
<Pressable onPress={handleAddField} style={[styles.modalButton, styles.saveButton]}>
|
||||
<Text style={styles.saveButtonText}>Qo'shish</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
padding: 16,
|
||||
gap: 16,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
divider: {
|
||||
width: 1.5,
|
||||
height: 24,
|
||||
backgroundColor: '#fff',
|
||||
marginLeft: 12,
|
||||
},
|
||||
prefix: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: '#fff',
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
prefixFocused: {
|
||||
color: '#fff',
|
||||
},
|
||||
prefixContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 12,
|
||||
},
|
||||
inputBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#1e293b',
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: '#334155',
|
||||
paddingHorizontal: 16,
|
||||
height: 56,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#1e293b',
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
gap: 16,
|
||||
},
|
||||
cardHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
editButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#1e40af20',
|
||||
},
|
||||
editButtonText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#3b82f6',
|
||||
},
|
||||
addButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#10b98120',
|
||||
},
|
||||
addButtonText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#10b981',
|
||||
},
|
||||
infoRow: {
|
||||
gap: 6,
|
||||
},
|
||||
label: {
|
||||
fontSize: 13,
|
||||
color: '#94a3b8',
|
||||
fontWeight: '500' as const,
|
||||
},
|
||||
value: {
|
||||
fontSize: 16,
|
||||
color: '#f8fafc',
|
||||
fontWeight: '600' as const,
|
||||
},
|
||||
fieldsContainer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
},
|
||||
fieldChip: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
fieldText: {
|
||||
fontSize: 14,
|
||||
color: '#f8fafc',
|
||||
fontWeight: '600' as const,
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: '#00000090',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
},
|
||||
modalContent: {
|
||||
width: '100%',
|
||||
maxWidth: 400,
|
||||
backgroundColor: '#1e293b',
|
||||
borderRadius: 20,
|
||||
padding: 24,
|
||||
gap: 16,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 20,
|
||||
fontWeight: '700' as const,
|
||||
color: '#f8fafc',
|
||||
marginBottom: 8,
|
||||
},
|
||||
inputLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#94a3b8',
|
||||
marginBottom: -8,
|
||||
},
|
||||
input: {
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 16,
|
||||
color: '#f8fafc',
|
||||
borderWidth: 1,
|
||||
borderColor: '#334155',
|
||||
},
|
||||
modalButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
marginTop: 8,
|
||||
},
|
||||
modalButton: {
|
||||
flex: 1,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 12,
|
||||
alignItems: 'center',
|
||||
},
|
||||
cancelButton: {
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
saveButton: {
|
||||
backgroundColor: '#3b82f6',
|
||||
},
|
||||
cancelButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
saveButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#ffffff',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user