Files
info-target-mobile/screens/profile/ui/ProductServicesTab.tsx
Samandar Turgunboyev ab363ca3b9 bug fixed
2026-03-02 13:22:55 +05:00

553 lines
14 KiB
TypeScript

import { Image as ExpoImage } from 'expo-image';
import { FileVideo, Image as ImageIcon, Plus } from 'lucide-react-native';
import { useState } from 'react';
import {
Alert,
FlatList,
Modal,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import { useProfileData } from '../lib/ProfileDataContext';
const CATEGORIES = [
'Qurilish',
'Savdo',
'IT',
'Dizayn',
'Transport',
"Ta'lim",
"Sog'liqni saqlash",
'Restoran',
'Turizm',
'Sport',
];
export function ProductServicesTab() {
const { productServices, addProductService } = useProfileData();
const [modalVisible, setModalVisible] = useState(false);
const [step, setStep] = useState<1 | 2>(1);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [mediaUrl, setMediaUrl] = useState('');
const [mediaType, setMediaType] = useState<'image' | 'video'>('image');
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const resetForm = () => {
setTitle('');
setDescription('');
setMediaUrl('');
setMediaType('image');
setSelectedCategories([]);
setStep(1);
};
const handleNext = () => {
if (!title.trim() || !description.trim() || !mediaUrl.trim()) {
Alert.alert('Xato', "Barcha maydonlarni to'ldiring");
return;
}
setStep(2);
};
const handleAdd = () => {
if (selectedCategories.length === 0) {
Alert.alert('Xato', 'Kamida bitta kategoriya tanlang');
return;
}
addProductService({
title: title.trim(),
description: description.trim(),
mediaUrl: mediaUrl.trim(),
mediaType,
categories: selectedCategories,
});
resetForm();
setModalVisible(false);
Alert.alert('Muvaffaqiyat', "Xizmat muvaffaqiyatli qo'shildi");
};
const toggleCategory = (category: string) => {
setSelectedCategories((prev) =>
prev.includes(category) ? prev.filter((c) => c !== category) : [...prev, category]
);
};
return (
<View style={styles.container}>
<Pressable onPress={() => setModalVisible(true)} style={styles.addButton}>
<Plus size={20} color="#ffffff" />
<Text style={styles.addButtonText}>Xizmat qo'shish</Text>
</Pressable>
<FlatList
data={productServices}
keyExtractor={(item) => item.id}
contentContainerStyle={styles.list}
renderItem={({ item }) => (
<View style={styles.card}>
<View style={styles.mediaContainer}>
{item.mediaType === 'image' ? (
<ExpoImage
source={{ uri: item.mediaUrl }}
style={styles.media}
contentFit="cover"
/>
) : (
<View style={styles.videoPlaceholder}>
<FileVideo size={40} color="#64748b" />
<Text style={styles.videoText}>Video</Text>
</View>
)}
</View>
<View style={styles.contentContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description} numberOfLines={2}>
{item.description}
</Text>
<View style={styles.categoriesContainer}>
{item.categories.map((category, index) => (
<View key={index} style={styles.categoryChip}>
<Text style={styles.categoryText}>{category}</Text>
</View>
))}
</View>
</View>
</View>
)}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<ImageIcon size={48} color="#475569" />
<Text style={styles.emptyText}>Hozircha xizmatlar yo'q</Text>
<Text style={styles.emptySubtext}>
Yangi xizmat qo'shish uchun yuqoridagi tugmani bosing
</Text>
</View>
}
/>
<Modal
visible={modalVisible}
transparent
animationType="slide"
onRequestClose={() => {
resetForm();
setModalVisible(false);
}}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<View style={styles.modalHeader}>
<Text style={styles.modalTitle}>
{step === 1 ? "Xizmat ma'lumotlari" : 'Kategoriyalarni tanlang'}
</Text>
<View style={styles.stepIndicator}>
<View style={[styles.stepDot, step >= 1 && styles.stepDotActive]} />
<View style={[styles.stepLine, step >= 2 && styles.stepLineActive]} />
<View style={[styles.stepDot, step >= 2 && styles.stepDotActive]} />
</View>
</View>
{step === 1 ? (
<ScrollView showsVerticalScrollIndicator={false}>
<Text style={styles.inputLabel}>Nomi</Text>
<TextInput
style={styles.input}
value={title}
onChangeText={setTitle}
placeholder="Xizmat nomi"
placeholderTextColor="#64748b"
/>
<Text style={styles.inputLabel}>Tavsif</Text>
<TextInput
style={[styles.input, styles.textArea]}
value={description}
onChangeText={setDescription}
placeholder="Xizmat haqida batafsil ma'lumot"
placeholderTextColor="#64748b"
multiline
numberOfLines={4}
textAlignVertical="top"
/>
<Text style={styles.inputLabel}>Media turi</Text>
<View style={styles.mediaTypeContainer}>
<Pressable
onPress={() => setMediaType('image')}
style={[
styles.mediaTypeButton,
mediaType === 'image' && styles.mediaTypeButtonActive,
]}
>
<ImageIcon size={20} color={mediaType === 'image' ? '#3b82f6' : '#64748b'} />
<Text
style={[
styles.mediaTypeText,
mediaType === 'image' && styles.mediaTypeTextActive,
]}
>
Rasm
</Text>
</Pressable>
<Pressable
onPress={() => setMediaType('video')}
style={[
styles.mediaTypeButton,
mediaType === 'video' && styles.mediaTypeButtonActive,
]}
>
<FileVideo size={20} color={mediaType === 'video' ? '#3b82f6' : '#64748b'} />
<Text
style={[
styles.mediaTypeText,
mediaType === 'video' && styles.mediaTypeTextActive,
]}
>
Video
</Text>
</Pressable>
</View>
<Text style={styles.inputLabel}>Media URL</Text>
<TextInput
style={styles.input}
value={mediaUrl}
onChangeText={setMediaUrl}
placeholder="https://..."
placeholderTextColor="#64748b"
keyboardType="url"
/>
<View style={styles.modalButtons}>
<Pressable
onPress={() => {
resetForm();
setModalVisible(false);
}}
style={[styles.modalButton, styles.cancelButton]}
>
<Text style={styles.cancelButtonText}>Bekor qilish</Text>
</Pressable>
<Pressable onPress={handleNext} style={[styles.modalButton, styles.nextButton]}>
<Text style={styles.nextButtonText}>Keyingisi</Text>
</Pressable>
</View>
</ScrollView>
) : (
<>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={styles.categoriesGrid}>
{CATEGORIES.map((category) => (
<Pressable
key={category}
onPress={() => toggleCategory(category)}
style={[
styles.categoryOption,
selectedCategories.includes(category) && styles.categoryOptionSelected,
]}
>
<Text
style={[
styles.categoryOptionText,
selectedCategories.includes(category) &&
styles.categoryOptionTextSelected,
]}
>
{category}
</Text>
</Pressable>
))}
</View>
</ScrollView>
<View style={styles.modalButtons}>
<Pressable
onPress={() => setStep(1)}
style={[styles.modalButton, styles.backButton]}
>
<Text style={styles.backButtonText}>Ortga</Text>
</Pressable>
<Pressable onPress={handleAdd} style={[styles.modalButton, styles.saveButton]}>
<Text style={styles.saveButtonText}>Qo'shish</Text>
</Pressable>
</View>
</>
)}
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
paddingBottom: 100,
},
addButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
backgroundColor: '#3b82f6',
paddingVertical: 14,
borderRadius: 12,
marginBottom: 16,
},
addButtonText: {
fontSize: 16,
fontWeight: '700' as const,
color: '#ffffff',
},
list: {
gap: 16,
flexGrow: 1
},
card: {
backgroundColor: '#1e293b',
borderRadius: 16,
overflow: 'hidden',
},
mediaContainer: {
width: '100%',
height: 200,
backgroundColor: '#334155',
},
media: {
width: '100%',
height: '100%',
},
videoPlaceholder: {
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
},
videoText: {
fontSize: 14,
color: '#64748b',
fontWeight: '600' as const,
},
contentContainer: {
padding: 16,
gap: 8,
},
title: {
fontSize: 18,
fontWeight: '700' as const,
color: '#f8fafc',
},
description: {
fontSize: 14,
color: '#94a3b8',
lineHeight: 20,
},
categoriesContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
marginTop: 4,
},
categoryChip: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 12,
backgroundColor: '#334155',
},
categoryText: {
fontSize: 12,
color: '#94a3b8',
fontWeight: '500' as const,
},
emptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 80,
gap: 12,
},
emptyText: {
fontSize: 18,
fontWeight: '600' as const,
color: '#64748b',
},
emptySubtext: {
fontSize: 14,
color: '#475569',
textAlign: 'center',
paddingHorizontal: 40,
},
modalOverlay: {
flex: 1,
backgroundColor: '#00000090',
justifyContent: 'flex-end',
},
modalContent: {
maxHeight: '90%',
backgroundColor: '#1e293b',
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
padding: 24,
gap: 20,
},
modalHeader: {
gap: 12,
},
modalTitle: {
fontSize: 22,
fontWeight: '700' as const,
color: '#f8fafc',
},
stepIndicator: {
flexDirection: 'row',
alignItems: 'center',
gap: 4,
},
stepDot: {
width: 10,
height: 10,
borderRadius: 5,
backgroundColor: '#334155',
},
stepDotActive: {
backgroundColor: '#3b82f6',
},
stepLine: {
flex: 1,
height: 2,
backgroundColor: '#334155',
},
stepLineActive: {
backgroundColor: '#3b82f6',
},
inputLabel: {
fontSize: 14,
fontWeight: '600' as const,
color: '#94a3b8',
marginBottom: 8,
marginTop: 8,
},
input: {
backgroundColor: '#0f172a',
borderRadius: 12,
padding: 14,
fontSize: 16,
color: '#f8fafc',
borderWidth: 1,
borderColor: '#334155',
},
textArea: {
height: 100,
paddingTop: 14,
},
mediaTypeContainer: {
flexDirection: 'row',
gap: 12,
},
mediaTypeButton: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
paddingVertical: 14,
borderRadius: 12,
backgroundColor: '#0f172a',
borderWidth: 1,
borderColor: '#334155',
},
mediaTypeButtonActive: {
borderColor: '#3b82f6',
backgroundColor: '#1e40af20',
},
mediaTypeText: {
fontSize: 14,
fontWeight: '600' as const,
color: '#64748b',
},
mediaTypeTextActive: {
color: '#3b82f6',
},
categoriesGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
},
categoryOption: {
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 12,
backgroundColor: '#0f172a',
borderWidth: 1,
borderColor: '#334155',
},
categoryOptionSelected: {
backgroundColor: '#1e40af20',
borderColor: '#3b82f6',
},
categoryOptionText: {
fontSize: 14,
fontWeight: '600' as const,
color: '#94a3b8',
},
categoryOptionTextSelected: {
color: '#3b82f6',
},
modalButtons: {
flexDirection: 'row',
gap: 12,
marginTop: 8,
},
modalButton: {
flex: 1,
paddingVertical: 14,
borderRadius: 12,
alignItems: 'center',
},
cancelButton: {
backgroundColor: '#334155',
},
backButton: {
backgroundColor: '#334155',
},
nextButton: {
backgroundColor: '#3b82f6',
},
saveButton: {
backgroundColor: '#10b981',
},
cancelButtonText: {
fontSize: 16,
fontWeight: '600' as const,
color: '#f8fafc',
},
backButtonText: {
fontSize: 16,
fontWeight: '600' as const,
color: '#f8fafc',
},
nextButtonText: {
fontSize: 16,
fontWeight: '600' as const,
color: '#ffffff',
},
saveButtonText: {
fontSize: 16,
fontWeight: '600' as const,
color: '#ffffff',
},
});