fitst commit

This commit is contained in:
Samandar Turgunboyev
2026-01-28 18:26:50 +05:00
parent 166a55b1e9
commit 124798419b
196 changed files with 26627 additions and 421 deletions

View 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', 'Malumotlar yangilandi');
},
onError: () => {
Alert.alert('Xatolik', 'Malumotlarni 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',
},
});